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