1 /*
2  * Copyright 2023 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_fs26_wdog
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/drivers/watchdog.h>
12 #include <zephyr/sys/byteorder.h>
13 
14 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(wdt_nxp_fs26);
17 
18 #include "wdt_nxp_fs26.h"
19 
20 #if defined(CONFIG_BIG_ENDIAN)
21 #define SWAP_ENDIANNESS
22 #endif
23 
24 #define FS26_CRC_TABLE_SIZE 256U
25 #define FS26_CRC_INIT 0xff
26 #define FS26_FS_WD_TOKEN_DEFAULT 0x5ab2
27 #define FS26_INIT_FS_TIMEOUT_MS 1000U
28 
29 /* Helper macros to set register values from Kconfig options */
30 #define WD_ERR_LIMIT(x)	_CONCAT(WD_ERR_LIMIT_, x)
31 #define WD_RFR_LIMIT(x)	_CONCAT(WD_RFR_LIMIT_, x)
32 #define WDW_PERIOD(x)	_CONCAT(_CONCAT(WDW_PERIOD_, x), MS)
33 
34 #define BAD_WD_REFRESH_ERROR_STRING(x)					\
35 	((((x) & BAD_WD_DATA) ? "error in the data" :			\
36 		(((x) & BAD_WD_TIMING) ? "error in the timing (window)"	\
37 		: "unknown error")))
38 
39 enum fs26_wd_type {
40 	FS26_WD_SIMPLE,
41 	FS26_WD_CHALLENGER
42 };
43 
44 struct fs26_spi_rx_frame {
45 	union {
46 		struct {
47 			uint8_t m_aval : 1;
48 			uint8_t fs_en  : 1;
49 			uint8_t fs_g   : 1;
50 			uint8_t com_g  : 1;
51 			uint8_t wio_g  : 1;
52 			uint8_t vsup_g : 1;
53 			uint8_t reg_g  : 1;
54 			uint8_t tsd_g  : 1;
55 		};
56 		uint8_t raw;
57 	} status;
58 	uint16_t data;
59 };
60 
61 struct fs26_spi_tx_frame {
62 	bool write;
63 	uint8_t addr;
64 	uint16_t data;
65 };
66 
67 struct wdt_nxp_fs26_config {
68 	struct spi_dt_spec spi;
69 	enum fs26_wd_type wd_type;
70 	struct gpio_dt_spec int_gpio;
71 };
72 
73 struct wdt_nxp_fs26_data {
74 	wdt_callback_t callback;
75 	uint16_t token;	/* local copy of the watchdog token */
76 	bool timeout_installed;
77 	uint8_t window_period;
78 	uint8_t window_duty_cycle;
79 	uint8_t fs_reaction;
80 	struct gpio_callback int_gpio_cb;
81 	struct k_sem int_sem;
82 	struct k_thread int_thread;
83 
84 	K_KERNEL_STACK_MEMBER(int_thread_stack, CONFIG_WDT_NXP_FS26_INT_THREAD_STACK_SIZE);
85 };
86 
87 /*
88  * Allowed values for watchdog period and duty cycle (CLOSED window).
89  * The index is the value to write to the register. Keep values in ascending order.
90  */
91 static const uint32_t fs26_period_values[] = {
92 	0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64, 128, 256, 512, 1024
93 };
94 
95 static const double fs26_dc_closed_values[] = {
96 	0.3125, 0.375, 0.5, 0.625, 0.6875, 0.75, 0.8125
97 };
98 
99 /* CRC lookup table */
100 static const uint8_t FS26_CRC_TABLE[FS26_CRC_TABLE_SIZE] = {
101 	0x00u, 0x1du, 0x3au, 0x27u, 0x74u, 0x69u, 0x4eu, 0x53u, 0xe8u,
102 	0xf5u, 0xd2u, 0xcfu, 0x9cu, 0x81u, 0xa6u, 0xbbu, 0xcdu, 0xd0u,
103 	0xf7u, 0xeau, 0xb9u, 0xa4u, 0x83u, 0x9eu, 0x25u, 0x38u, 0x1fu,
104 	0x02u, 0x51u, 0x4cu, 0x6bu, 0x76u, 0x87u, 0x9au, 0xbdu, 0xa0u,
105 	0xf3u, 0xeeu, 0xc9u, 0xd4u, 0x6fu, 0x72u, 0x55u, 0x48u, 0x1bu,
106 	0x06u, 0x21u, 0x3cu, 0x4au, 0x57u, 0x70u, 0x6du, 0x3eu, 0x23u,
107 	0x04u, 0x19u, 0xa2u, 0xbfu, 0x98u, 0x85u, 0xd6u, 0xcbu, 0xecu,
108 	0xf1u, 0x13u, 0x0eu, 0x29u, 0x34u, 0x67u, 0x7au, 0x5du, 0x40u,
109 	0xfbu, 0xe6u, 0xc1u, 0xdcu, 0x8fu, 0x92u, 0xb5u, 0xa8u, 0xdeu,
110 	0xc3u, 0xe4u, 0xf9u, 0xaau, 0xb7u, 0x90u, 0x8du, 0x36u, 0x2bu,
111 	0x0cu, 0x11u, 0x42u, 0x5fu, 0x78u, 0x65u, 0x94u, 0x89u, 0xaeu,
112 	0xb3u, 0xe0u, 0xfdu, 0xdau, 0xc7u, 0x7cu, 0x61u, 0x46u, 0x5bu,
113 	0x08u, 0x15u, 0x32u, 0x2fu, 0x59u, 0x44u, 0x63u, 0x7eu, 0x2du,
114 	0x30u, 0x17u, 0x0au, 0xb1u, 0xacu, 0x8bu, 0x96u, 0xc5u, 0xd8u,
115 	0xffu, 0xe2u, 0x26u, 0x3bu, 0x1cu, 0x01u, 0x52u, 0x4fu, 0x68u,
116 	0x75u, 0xceu, 0xd3u, 0xf4u, 0xe9u, 0xbau, 0xa7u, 0x80u, 0x9du,
117 	0xebu, 0xf6u, 0xd1u, 0xccu, 0x9fu, 0x82u, 0xa5u, 0xb8u, 0x03u,
118 	0x1eu, 0x39u, 0x24u, 0x77u, 0x6au, 0x4du, 0x50u, 0xa1u, 0xbcu,
119 	0x9bu, 0x86u, 0xd5u, 0xc8u, 0xefu, 0xf2u, 0x49u, 0x54u, 0x73u,
120 	0x6eu, 0x3du, 0x20u, 0x07u, 0x1au, 0x6cu, 0x71u, 0x56u, 0x4bu,
121 	0x18u, 0x05u, 0x22u, 0x3fu, 0x84u, 0x99u, 0xbeu, 0xa3u, 0xf0u,
122 	0xedu, 0xcau, 0xd7u, 0x35u, 0x28u, 0x0fu, 0x12u, 0x41u, 0x5cu,
123 	0x7bu, 0x66u, 0xddu, 0xc0u, 0xe7u, 0xfau, 0xa9u, 0xb4u, 0x93u,
124 	0x8eu, 0xf8u, 0xe5u, 0xc2u, 0xdfu, 0x8cu, 0x91u, 0xb6u, 0xabu,
125 	0x10u, 0x0du, 0x2au, 0x37u, 0x64u, 0x79u, 0x5eu, 0x43u, 0xb2u,
126 	0xafu, 0x88u, 0x95u, 0xc6u, 0xdbu, 0xfcu, 0xe1u, 0x5au, 0x47u,
127 	0x60u, 0x7du, 0x2eu, 0x33u, 0x14u, 0x09u, 0x7fu, 0x62u, 0x45u,
128 	0x58u, 0x0bu, 0x16u, 0x31u, 0x2cu, 0x97u, 0x8au, 0xadu, 0xb0u,
129 	0xe3u, 0xfeu, 0xd9u, 0xc4u
130 };
131 
fs26_calcrc(const uint8_t * data,size_t size)132 static uint8_t fs26_calcrc(const uint8_t *data, size_t size)
133 {
134 	uint8_t crc;
135 	uint8_t tableidx;
136 	uint8_t i;
137 
138 	/* Set CRC token value */
139 	crc = FS26_CRC_INIT;
140 
141 	for (i = size; i > 0; i--) {
142 		tableidx = crc ^ data[i];
143 		crc = FS26_CRC_TABLE[tableidx];
144 	}
145 
146 	return crc;
147 }
148 
fs26_spi_transceive(const struct spi_dt_spec * spi,struct fs26_spi_tx_frame * tx_frame,struct fs26_spi_rx_frame * rx_frame)149 static int fs26_spi_transceive(const struct spi_dt_spec *spi,
150 			       struct fs26_spi_tx_frame *tx_frame,
151 			       struct fs26_spi_rx_frame *rx_frame)
152 {
153 	uint32_t tx_buf;
154 	uint32_t rx_buf;
155 	uint8_t crc;
156 	int retval;
157 
158 	struct spi_buf spi_tx_buf = {
159 		.buf = &tx_buf,
160 		.len = sizeof(tx_buf)
161 	};
162 	struct spi_buf spi_rx_buf = {
163 		.buf = &rx_buf,
164 		.len = sizeof(rx_buf)
165 	};
166 	struct spi_buf_set spi_tx_set = {
167 		.buffers = &spi_tx_buf,
168 		.count = 1U
169 	};
170 	struct spi_buf_set spi_rx_set = {
171 		.buffers = &spi_rx_buf,
172 		.count = 1U
173 	};
174 
175 	/* Create frame to Tx, always for Fail Safe */
176 	tx_buf = (uint32_t)(FS26_SET_REG_ADDR(tx_frame->addr)
177 		| FS26_SET_DATA(tx_frame->data)
178 		| (tx_frame->write ? FS26_RW : 0));
179 
180 	crc = fs26_calcrc((uint8_t *)&tx_buf, sizeof(tx_buf) - 1);
181 	tx_buf |= (uint32_t)FS26_SET_CRC(crc);
182 
183 #if defined(SWAP_ENDIANNESS)
184 	tx_buf = __builtin_bswap32(tx_buf);
185 #endif
186 
187 	retval = spi_transceive_dt(spi, &spi_tx_set, &spi_rx_set);
188 	if (retval) {
189 		goto error;
190 	}
191 
192 #if defined(SWAP_ENDIANNESS)
193 	rx_buf = __builtin_bswap32(rx_buf);
194 #endif
195 
196 	/* Verify CRC of Rx frame */
197 	crc = fs26_calcrc((uint8_t *)&rx_buf, sizeof(rx_buf) - 1);
198 	if (crc != ((uint8_t)FS26_GET_CRC(rx_buf))) {
199 		LOG_ERR("Rx invalid CRC");
200 		retval = -EIO;
201 		goto error;
202 	}
203 
204 	if (rx_frame) {
205 		rx_frame->status.raw = (uint8_t)FS26_GET_DEV_STATUS(rx_buf);
206 		rx_frame->data = (uint16_t)FS26_GET_DATA(rx_buf);
207 	}
208 
209 error:
210 	return retval;
211 }
212 
213 /**
214  * @brief Get value of register with address @p addr
215  *
216  * @param spi SPI specs for interacting with the device
217  * @param addr Register address
218  * @param rx_frame SPI frame containing read data and device status flags
219  *
220  * @return 0 on success, error code otherwise
221  */
fs26_getreg(const struct spi_dt_spec * spi,uint8_t addr,struct fs26_spi_rx_frame * rx_frame)222 static int fs26_getreg(const struct spi_dt_spec *spi, uint8_t addr,
223 		       struct fs26_spi_rx_frame *rx_frame)
224 {
225 	struct fs26_spi_tx_frame tx_frame = {
226 		.addr = addr,
227 		.write = 0,
228 		.data = 0
229 	};
230 
231 	return fs26_spi_transceive(spi, &tx_frame, rx_frame);
232 }
233 
234 /**
235  * @brief Set @p regval value in register with address @p addr
236  *
237  * @param spi SPI specs for interacting with the device
238  * @param addr Register address
239  * @param regval Register value to set
240  *
241  * @return 0 on success, error code otherwise
242  */
fs26_setreg(const struct spi_dt_spec * spi,uint8_t addr,uint16_t regval)243 static int fs26_setreg(const struct spi_dt_spec *spi, uint8_t addr, uint16_t regval)
244 {
245 	struct fs26_spi_tx_frame tx_frame = {
246 		.addr = addr,
247 		.write = true,
248 		.data = regval
249 	};
250 
251 	return fs26_spi_transceive(spi, &tx_frame, NULL);
252 }
253 
254 /**
255  * @brief Calculate watchdog answer based on received token
256  *
257  * @return answer value to write to FS_WD_ANSWER
258  */
fs26_wd_compute_answer(uint16_t token)259 static inline uint16_t fs26_wd_compute_answer(uint16_t token)
260 {
261 	uint32_t tmp = token;
262 
263 	tmp *= 4U;
264 	tmp += 6U;
265 	tmp -= 4U;
266 	tmp = ~tmp;
267 	tmp /= 4U;
268 
269 	return (uint16_t)tmp;
270 }
271 
272 /**
273  * @brief Refresh the watchdog and verify the refresh was good.
274  *
275  * @return 0 on success, error code otherwise
276  */
fs26_wd_refresh(const struct device * dev)277 static int fs26_wd_refresh(const struct device *dev)
278 {
279 	const struct wdt_nxp_fs26_config *config = dev->config;
280 	struct wdt_nxp_fs26_data *data = dev->data;
281 	int retval = 0;
282 	int key;
283 	uint16_t answer;
284 	struct fs26_spi_rx_frame rx_frame;
285 
286 	if (config->wd_type == FS26_WD_SIMPLE) {
287 		if (fs26_setreg(&config->spi, FS26_FS_WD_ANSWER, data->token) == 0) {
288 			LOG_ERR("Failed to write answer");
289 			retval = -EIO;
290 		}
291 	} else if (config->wd_type == FS26_WD_CHALLENGER) {
292 		key = irq_lock();
293 
294 		/* Read challenge token generated by the device */
295 		if (fs26_getreg(&config->spi, FS26_FS_WD_TOKEN, &rx_frame)) {
296 			LOG_ERR("Failed to obtain watchdog token");
297 			retval = -EIO;
298 		} else {
299 			data->token = rx_frame.data;
300 			LOG_DBG("Watchdog token is %x", data->token);
301 
302 			answer = fs26_wd_compute_answer(data->token);
303 			if (fs26_setreg(&config->spi, FS26_FS_WD_ANSWER, answer)) {
304 				LOG_ERR("Failed to write answer");
305 				retval = -EIO;
306 			}
307 		}
308 
309 		irq_unlock(key);
310 	} else {
311 		retval = -EINVAL;
312 	}
313 
314 	/* Check if watchdog refresh was successful */
315 	if (!retval) {
316 		if (!fs26_getreg(&config->spi, FS26_FS_GRL_FLAGS, &rx_frame)) {
317 			if ((rx_frame.data & FS_WD_G_MASK) == FS_WD_G) {
318 				if (!fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame)) {
319 					LOG_ERR("Bad watchdog refresh, %s",
320 						BAD_WD_REFRESH_ERROR_STRING(rx_frame.data));
321 				}
322 				retval = -EIO;
323 			} else {
324 				LOG_DBG("Refreshed the watchdog");
325 			}
326 		}
327 	}
328 
329 	return retval;
330 }
331 
332 /**
333  * @brief Wait for state machine to be at in INIT_FS state
334  *
335  * @return 0 on success, -ETIMEDOUT if timedout
336  */
fs26_poll_for_init_fs_state(const struct device * dev)337 static int fs26_poll_for_init_fs_state(const struct device *dev)
338 {
339 	const struct wdt_nxp_fs26_config *config = dev->config;
340 	struct fs26_spi_rx_frame rx_frame;
341 	uint32_t regval = 0;
342 	int64_t timeout;
343 	int64_t now;
344 
345 	timeout = k_uptime_get() + FS26_INIT_FS_TIMEOUT_MS;
346 
347 	do {
348 		if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) {
349 			regval = rx_frame.data;
350 		}
351 		k_sleep(K_MSEC(1));
352 		now = k_uptime_get();
353 	} while ((now < timeout) && (regval & FS_STATES_MASK) != FS_STATES_INIT_FS);
354 
355 	if (now >= timeout) {
356 		LOG_ERR("Timedout waiting for INIT_FS state");
357 		return -ETIMEDOUT;
358 	}
359 
360 	return 0;
361 }
362 
363 /**
364  * @brief Go to INIT_FS state from any FS state after INIT_FS
365  *
366  * After INIT_FS closure, it is possible to come back to INIT_FS with the
367  * GOTO_INIT bit in FS_SAFE_IOS_1 register from any FS state after INIT_FS.
368  *
369  * @return 0 on success, error code otherwise
370  */
fs26_goto_init_fs_state(const struct device * dev)371 static int fs26_goto_init_fs_state(const struct device *dev)
372 {
373 	const struct wdt_nxp_fs26_config *config = dev->config;
374 	struct fs26_spi_rx_frame rx_frame;
375 	uint32_t current_state;
376 	int retval = -EIO;
377 
378 	if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) {
379 		current_state = rx_frame.data & FS_STATES_MASK;
380 		if (current_state < FS_STATES_INIT_FS) {
381 			LOG_ERR("Cannot go to INIT_FS from current state %x", current_state);
382 			retval = -EIO;
383 		} else if (current_state == FS_STATES_INIT_FS) {
384 			retval = 0;
385 		} else {
386 			fs26_setreg(&config->spi, FS26_FS_SAFE_IOS_1, (uint32_t)FS_GOTO_INIT);
387 			retval = fs26_poll_for_init_fs_state(dev);
388 		}
389 	}
390 
391 	return retval;
392 }
393 
394 /**
395  * @brief Close INIT_FS phase with a (good) watchdog refresh.
396  *
397  * @return 0 on success, error code otherwise
398  */
fs26_exit_init_fs_state(const struct device * dev)399 static inline int fs26_exit_init_fs_state(const struct device *dev)
400 {
401 	return fs26_wd_refresh(dev);
402 }
403 
wdt_nxp_fs26_feed(const struct device * dev,int channel_id)404 static int wdt_nxp_fs26_feed(const struct device *dev, int channel_id)
405 {
406 	struct wdt_nxp_fs26_data *data = dev->data;
407 
408 	if (channel_id != 0) {
409 		LOG_ERR("Invalid channel ID");
410 		return -EINVAL;
411 	}
412 
413 	if (!data->timeout_installed) {
414 		LOG_ERR("No timeout installed");
415 		return -EINVAL;
416 	}
417 
418 	return fs26_wd_refresh(dev);
419 }
420 
wdt_nxp_fs26_setup(const struct device * dev,uint8_t options)421 static int wdt_nxp_fs26_setup(const struct device *dev, uint8_t options)
422 {
423 	const struct wdt_nxp_fs26_config *config = dev->config;
424 	struct wdt_nxp_fs26_data *data = dev->data;
425 	uint32_t regval;
426 
427 	if (!data->timeout_installed) {
428 		LOG_ERR("No timeout installed");
429 		return -EINVAL;
430 	}
431 
432 	if ((options & WDT_OPT_PAUSE_IN_SLEEP) || (options & WDT_OPT_PAUSE_HALTED_BY_DBG)) {
433 		return -ENOTSUP;
434 	}
435 
436 	/*
437 	 * Apply fail-safe reaction configuration on RSTB and/or the safety output(s),
438 	 * configurable during the initialization phase.
439 	 */
440 	if (fs26_goto_init_fs_state(dev)) {
441 		LOG_ERR("Failed to go to INIT_FS");
442 		return -EIO;
443 	}
444 
445 	regval = WD_ERR_LIMIT(CONFIG_WDT_NXP_FS26_ERROR_COUNTER_LIMIT)
446 		| WD_RFR_LIMIT(CONFIG_WDT_NXP_FS26_REFRESH_COUNTER_LIMIT)
447 		| ((data->fs_reaction << WD_FS_REACTION_SHIFT) & WD_FS_REACTION_MASK);
448 
449 	fs26_setreg(&config->spi, FS26_FS_I_WD_CFG, regval);
450 	fs26_setreg(&config->spi, FS26_FS_I_NOT_WD_CFG, ~regval);
451 
452 	/* Apply watchdog window configuration, configurable during any FS state */
453 	regval = ((data->window_period << WDW_PERIOD_SHIFT) & WDW_PERIOD_MASK)
454 		| ((data->window_duty_cycle << WDW_DC_SHIFT) & WDW_DC_MASK)
455 		| WDW_RECOVERY_DISABLE;
456 
457 	fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval);
458 	fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval);
459 
460 	/*
461 	 * The new watchdog window is effective after the next watchdog refresh,
462 	 * so feed the watchdog once to make it effective after exiting this
463 	 * function. Also it's required to close init phase.
464 	 */
465 	if (fs26_exit_init_fs_state(dev)) {
466 		LOG_ERR("Failed to close INIT_FS");
467 		return -EIO;
468 	}
469 
470 	return 0;
471 }
472 
wdt_nxp_fs26_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)473 static int wdt_nxp_fs26_install_timeout(const struct device *dev,
474 					const struct wdt_timeout_cfg *cfg)
475 {
476 	struct wdt_nxp_fs26_data *data = dev->data;
477 	uint32_t window_min;
478 	uint8_t i;
479 
480 	if (data->timeout_installed) {
481 		LOG_ERR("No more timeouts can be installed");
482 		return -ENOMEM;
483 	}
484 
485 	if ((cfg->window.max == 0) || (cfg->window.max > 1024)
486 			|| (cfg->window.max <= cfg->window.min)) {
487 		LOG_ERR("Invalid timeout value");
488 		return -EINVAL;
489 	}
490 
491 	/* Find nearest period value (rounded up) */
492 	for (i = 0; i < ARRAY_SIZE(fs26_period_values); i++) {
493 		if (fs26_period_values[i] >= cfg->window.max) {
494 			break;
495 		}
496 	}
497 	data->window_period = i;
498 	LOG_DBG("window.max requested %d ms, using %d ms",
499 		cfg->window.max, fs26_period_values[data->window_period]);
500 
501 	/*
502 	 * Find nearest duty cycle value based on new period, that results in a
503 	 * window's minimum near the requested (rounded up)
504 	 */
505 	for (i = 0; i < ARRAY_SIZE(fs26_dc_closed_values); i++) {
506 		window_min = (uint32_t)(fs26_dc_closed_values[i]
507 					* fs26_period_values[data->window_period]);
508 		if (window_min >= cfg->window.min) {
509 			break;
510 		}
511 	}
512 	if (i >= ARRAY_SIZE(fs26_dc_closed_values)) {
513 		LOG_ERR("Watchdog opened window too small");
514 		return -EINVAL;
515 	}
516 	data->window_duty_cycle = i;
517 
518 	LOG_DBG("window.min requested %d ms, using %d ms (%.2f%%)",
519 		cfg->window.min, window_min,
520 		fs26_dc_closed_values[data->window_duty_cycle] * 100);
521 
522 	/* Fail-safe reaction configuration */
523 	switch (cfg->flags) {
524 	case WDT_FLAG_RESET_SOC:
525 		__fallthrough;
526 	case WDT_FLAG_RESET_CPU_CORE:
527 		data->fs_reaction = WD_FS_REACTION_RSTB_FS0B >> WD_FS_REACTION_SHIFT;
528 		LOG_DBG("Configuring reset mode");
529 		break;
530 	case WDT_FLAG_RESET_NONE:
531 		data->fs_reaction = WD_FS_REACTION_NO_ACTION >> WD_FS_REACTION_SHIFT;
532 		LOG_DBG("Configuring non-reset mode");
533 		break;
534 	default:
535 		LOG_ERR("Unsupported watchdog configuration flag");
536 		return -EINVAL;
537 	}
538 
539 	data->callback = cfg->callback;
540 	data->timeout_installed = true;
541 
542 	/* Always return channel ID equal to 0 */
543 	return 0;
544 }
545 
wdt_nxp_fs26_disable(const struct device * dev)546 static int wdt_nxp_fs26_disable(const struct device *dev)
547 {
548 	const struct wdt_nxp_fs26_config *config = dev->config;
549 	struct wdt_nxp_fs26_data *data = dev->data;
550 	struct fs26_spi_rx_frame rx_frame;
551 	uint32_t regval;
552 
553 	if (fs26_getreg(&config->spi, FS26_FS_WDW_DURATION, &rx_frame)) {
554 		return -EIO;
555 	}
556 	if ((rx_frame.data & WDW_PERIOD_MASK) == WDW_PERIOD_DISABLE) {
557 		LOG_ERR("Watchdog already disabled");
558 		return -EFAULT;
559 	}
560 
561 	/* The watchdog window can be disabled only during the initialization phase */
562 	if (fs26_goto_init_fs_state(dev)) {
563 		LOG_ERR("Failed to go to INIT_FS");
564 		return -EIO;
565 	}
566 
567 	regval = WDW_PERIOD_DISABLE | WDW_RECOVERY_DISABLE;
568 	fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval);
569 	fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval);
570 
571 	/* The watchdog disabling is effective when the initialization phase is closed */
572 	if (fs26_exit_init_fs_state(dev)) {
573 		LOG_ERR("Failed to close INIT_FS");
574 		return -EIO;
575 	}
576 
577 	LOG_DBG("Watchdog disabled");
578 	data->timeout_installed = false;
579 
580 	return 0;
581 }
582 
wdt_nxp_fs26_int_thread(void * p1,void * p2,void * p3)583 static void wdt_nxp_fs26_int_thread(void *p1, void *p2, void *p3)
584 {
585 	ARG_UNUSED(p2);
586 	ARG_UNUSED(p3);
587 
588 	const struct device *dev = p1;
589 	const struct wdt_nxp_fs26_config *config = dev->config;
590 	struct wdt_nxp_fs26_data *data = dev->data;
591 	struct fs26_spi_rx_frame rx_frame;
592 	uint32_t regval;
593 
594 	while (1) {
595 		k_sem_take(&data->int_sem, K_FOREVER);
596 
597 		if ((!fs26_getreg(&config->spi, FS26_FS_GRL_FLAGS, &rx_frame))
598 			&& ((rx_frame.data & FS_WD_G_MASK) == FS_WD_G)) {
599 
600 			if ((!fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame))
601 				&& (rx_frame.data & BAD_WD_TIMING)) {
602 
603 				/* Clear flag */
604 				regval = BAD_WD_TIMING;
605 				fs26_setreg(&config->spi, FS26_FS_DIAG_SAFETY1, regval);
606 
607 				/* Invoke user callback */
608 				if (data->callback && data->timeout_installed) {
609 					data->callback(dev, 0);
610 				}
611 			}
612 		}
613 	}
614 }
615 
wdt_nxp_fs26_int_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)616 static void wdt_nxp_fs26_int_callback(const struct device *dev,
617 				      struct gpio_callback *cb,
618 				      uint32_t pins)
619 {
620 	struct wdt_nxp_fs26_data *data = CONTAINER_OF(cb, struct wdt_nxp_fs26_data,
621 						      int_gpio_cb);
622 
623 	ARG_UNUSED(dev);
624 	ARG_UNUSED(pins);
625 
626 	k_sem_give(&data->int_sem);
627 }
628 
wdt_nxp_fs26_init(const struct device * dev)629 static int wdt_nxp_fs26_init(const struct device *dev)
630 {
631 	const struct wdt_nxp_fs26_config *config = dev->config;
632 	struct wdt_nxp_fs26_data *data = dev->data;
633 	struct fs26_spi_rx_frame rx_frame;
634 	uint32_t regval;
635 
636 	/* Validate bus is ready */
637 	if (!spi_is_ready_dt(&config->spi)) {
638 		return -ENODEV;
639 	}
640 
641 	k_sem_init(&data->int_sem, 0, 1);
642 
643 	/* Configure GPIO used for INTB signal */
644 	if (!gpio_is_ready_dt(&config->int_gpio)) {
645 		LOG_ERR("GPIO port %s not ready", config->int_gpio.port->name);
646 		return -ENODEV;
647 	}
648 
649 	if (gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT)) {
650 		LOG_ERR("Unable to configure GPIO pin %u", config->int_gpio.pin);
651 		return -EIO;
652 	}
653 
654 	gpio_init_callback(&(data->int_gpio_cb), wdt_nxp_fs26_int_callback,
655 			   BIT(config->int_gpio.pin));
656 
657 	if (gpio_add_callback(config->int_gpio.port, &(data->int_gpio_cb))) {
658 		return -EINVAL;
659 	}
660 
661 	if (gpio_pin_interrupt_configure_dt(&config->int_gpio,
662 					    GPIO_INT_EDGE_FALLING)) {
663 		return -EINVAL;
664 	}
665 
666 	k_thread_create(&data->int_thread, data->int_thread_stack,
667 			CONFIG_WDT_NXP_FS26_INT_THREAD_STACK_SIZE,
668 			wdt_nxp_fs26_int_thread,
669 			(void *)dev, NULL, NULL,
670 			K_PRIO_COOP(CONFIG_WDT_NXP_FS26_INT_THREAD_PRIO),
671 			0, K_NO_WAIT);
672 
673 	/* Verify FS BIST before proceeding */
674 	if (fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame)) {
675 		return -EIO;
676 	}
677 
678 	if ((rx_frame.data & (ABIST1_PASS_MASK | LBIST_STATUS_MASK))
679 		!= (ABIST1_PASS | LBIST_STATUS_OK)) {
680 
681 		LOG_ERR("BIST failed 0x%x", rx_frame.data);
682 		return -EIO;
683 	}
684 
685 	/* Get FS state machine state */
686 	if (fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) {
687 		return -EIO;
688 	}
689 
690 	/* Verify if in DEBUG mode */
691 	if ((rx_frame.data & DBG_MODE_MASK) == DBG_MODE) {
692 		if (IS_ENABLED(CONFIG_WDT_NXP_FS26_EXIT_DEBUG_MODE)) {
693 			LOG_DBG("Exiting DEBUG mode");
694 			regval = rx_frame.data | EXIT_DBG_MODE;
695 			fs26_setreg(&config->spi, FS26_FS_STATES, regval);
696 		} else {
697 			LOG_ERR("In DEBUG mode, watchdog is disabled");
698 			return -EIO;
699 		}
700 	}
701 
702 	/* Go to INIT_FS state, if not already there */
703 	if (fs26_goto_init_fs_state(dev)) {
704 		LOG_ERR("Failed to go to INIT_FS");
705 		return -EIO;
706 	}
707 
708 	/* Clear pending FS diagnostic flags before initializing */
709 	regval = BAD_WD_DATA | BAD_WD_TIMING | ABIST2_PASS | ABIST2_DONE
710 		| SPI_FS_CLK | SPI_FS_REQ | SPI_FS_CRC | FS_OSC_DRIFT;
711 	fs26_setreg(&config->spi, FS26_FS_DIAG_SAFETY1, regval);
712 
713 	/*
714 	 * Perform the following sequence for all INIT_FS registers (FS_I_xxxx)
715 	 * - Write the desired data in the FS_I_Register_A (data)
716 	 * - Write the opposite in the FS_I_NOT_Register_A (~data)
717 	 */
718 
719 	/* OVUV_SAFE_REACTION1 */
720 	regval = VMON_PRE_OV_FS_REACTION_NO_EFFECT |
721 		VMON_PRE_UV_FS_REACTION_NO_EFFECT |
722 		VMON_CORE_OV_FS_REACTION_NO_EFFECT |
723 		VMON_CORE_UV_FS_REACTION_NO_EFFECT |
724 		VMON_LDO1_OV_FS_REACTION_NO_EFFECT |
725 		VMON_LDO1_UV_FS_REACTION_NO_EFFECT |
726 		VMON_LDO2_OV_FS_REACTION_NO_EFFECT |
727 		VMON_LDO2_UV_FS_REACTION_NO_EFFECT;
728 
729 	fs26_setreg(&config->spi, FS26_FS_I_OVUV_SAFE_REACTION1, regval);
730 	fs26_setreg(&config->spi, FS26_FS_I_NOT_OVUV_SAFE_REACTION1, ~regval);
731 
732 	/* OVUV_SAFE_REACTION2 */
733 	regval = VMON_EXT_OV_FS_REACTION_NO_EFFECT |
734 		VMON_EXT_UV_FS_REACTION_NO_EFFECT |
735 		VMON_REF_OV_FS_REACTION_NO_EFFECT |
736 		VMON_REF_UV_FS_REACTION_NO_EFFECT |
737 		VMON_TRK2_OV_FS_REACTION_NO_EFFECT |
738 		VMON_TRK2_UV_FS_REACTION_NO_EFFECT |
739 		VMON_TRK1_OV_FS_REACTION_NO_EFFECT |
740 		VMON_TRK1_UV_FS_REACTION_NO_EFFECT;
741 
742 	fs26_setreg(&config->spi, FS26_FS_I_OVUV_SAFE_REACTION2, regval);
743 	fs26_setreg(&config->spi, FS26_FS_I_NOT_OVUV_SAFE_REACTION2, ~regval);
744 
745 	/* FS_I_SAFE_INPUTS */
746 	regval = FCCU_CFG_NO_MONITORING | ERRMON_ACK_TIME_32MS;
747 
748 	fs26_setreg(&config->spi, FS26_FS_I_SAFE_INPUTS, regval);
749 	fs26_setreg(&config->spi, FS26_FS_I_NOT_SAFE_INPUTS, ~regval);
750 
751 	/* FS_I_FSSM */
752 	regval = FLT_ERR_REACTION_NO_EFFECT | CLK_MON_DIS | DIS8S;
753 
754 	fs26_setreg(&config->spi, FS26_FS_I_FSSM, regval);
755 	fs26_setreg(&config->spi, FS26_FS_I_NOT_FSSM, ~regval);
756 
757 	/* FS_I_WD_CFG */
758 	regval = WD_ERR_LIMIT(CONFIG_WDT_NXP_FS26_ERROR_COUNTER_LIMIT)
759 		| WD_RFR_LIMIT(CONFIG_WDT_NXP_FS26_REFRESH_COUNTER_LIMIT)
760 		| WD_FS_REACTION_NO_ACTION;
761 
762 	fs26_setreg(&config->spi, FS26_FS_I_WD_CFG, regval);
763 	fs26_setreg(&config->spi, FS26_FS_I_NOT_WD_CFG, ~regval);
764 
765 	/* FS_WDW_DURATION */
766 	/* Watchdog always disabled at boot */
767 	regval = WDW_PERIOD_DISABLE | WDW_RECOVERY_DISABLE;
768 
769 	fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval);
770 	fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval);
771 
772 	/* Set watchdog seed if not using the default */
773 	if (data->token != FS26_FS_WD_TOKEN_DEFAULT) {
774 		LOG_DBG("Set seed to %x", data->token);
775 		fs26_setreg(&config->spi, FS26_FS_WD_TOKEN, data->token);
776 	}
777 
778 	/* Mask all Fail-Safe interrupt sources except for watchdog bad refresh */
779 	regval = ~BAD_WD_M;
780 	fs26_setreg(&config->spi, FS26_FS_INTB_MASK, regval);
781 
782 	/* Mask all main interrupt souces */
783 	regval = 0xffff;
784 	fs26_setreg(&config->spi, FS26_M_TSD_MSK, regval);
785 	fs26_setreg(&config->spi, FS26_M_REG_MSK, regval);
786 	fs26_setreg(&config->spi, FS26_M_VSUP_MSK, regval);
787 	fs26_setreg(&config->spi, FS26_M_WIO_MSK, regval);
788 	fs26_setreg(&config->spi, FS26_M_COM_MSK, regval);
789 
790 	/* INIT_FS must be closed before the 256 ms timeout */
791 	if (fs26_exit_init_fs_state(dev)) {
792 		LOG_ERR("Failed to close INIT_FS");
793 		return -EIO;
794 	}
795 
796 	/* After INIT_FS is completed, check for data corruption in init registers */
797 	if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) {
798 		if ((rx_frame.data & REG_CORRUPT_MASK) == REG_CORRUPT) {
799 			LOG_ERR("Data content corruption detected in init registers");
800 			return -EIO;
801 		}
802 	}
803 
804 	return 0;
805 }
806 
807 static const struct wdt_driver_api wdt_nxp_fs26_api = {
808 	.setup = wdt_nxp_fs26_setup,
809 	.disable = wdt_nxp_fs26_disable,
810 	.install_timeout = wdt_nxp_fs26_install_timeout,
811 	.feed = wdt_nxp_fs26_feed,
812 };
813 
814 #define FS26_WDT_DEVICE_INIT(n)								\
815 	COND_CODE_1(DT_INST_ENUM_IDX(n, type),						\
816 		(BUILD_ASSERT(CONFIG_WDT_NXP_FS26_SEED != 0x0,				\
817 				"Seed value 0x0000 is not allowed");),			\
818 		(BUILD_ASSERT((CONFIG_WDT_NXP_FS26_SEED != 0x0)				\
819 				&& (CONFIG_WDT_NXP_FS26_SEED != 0xffff),		\
820 				"Seed values 0x0000 and 0xffff are not allowed");))	\
821 											\
822 	static struct wdt_nxp_fs26_data wdt_nxp_fs26_data_##n = {			\
823 		.token = CONFIG_WDT_NXP_FS26_SEED,					\
824 	};										\
825 											\
826 	static const struct wdt_nxp_fs26_config wdt_nxp_fs26_config_##n = {		\
827 		.spi = SPI_DT_SPEC_INST_GET(n,						\
828 			SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(32), 0),	\
829 		.wd_type = _CONCAT(FS26_WD_, DT_INST_STRING_UPPER_TOKEN(n, type)),	\
830 		.int_gpio = GPIO_DT_SPEC_INST_GET(n, int_gpios),			\
831 	};										\
832 											\
833 	DEVICE_DT_INST_DEFINE(n,							\
834 			      wdt_nxp_fs26_init,					\
835 			      NULL,							\
836 			      &wdt_nxp_fs26_data_##n,					\
837 			      &wdt_nxp_fs26_config_##n,					\
838 			      POST_KERNEL,						\
839 			      CONFIG_WDT_NXP_FS26_INIT_PRIORITY,			\
840 			      &wdt_nxp_fs26_api);
841 
842 DT_INST_FOREACH_STATUS_OKAY(FS26_WDT_DEVICE_INIT)
843