diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index 0870f0a66ef..8faf4c82fbe 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -42,5 +42,6 @@ zephyr_library_sources_ifdef(CONFIG_WDT_OPENTITAN wdt_opentitan.c) zephyr_library_sources_ifdef(CONFIG_WDT_DW wdt_dw.c wdt_dw_common.c) zephyr_library_sources_ifdef(CONFIG_WDT_INTEL_ADSP wdt_intel_adsp.c wdt_dw_common.c) zephyr_library_sources_ifdef(CONFIG_WDT_ANDES_ATCWDT200 wdt_andes_atcwdt200.c) +zephyr_library_sources_ifdef(CONFIG_WDT_NXP_FS26 wdt_nxp_fs26.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 52359b1cd77..4f01881fd09 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -114,4 +114,6 @@ source "drivers/watchdog/Kconfig.opentitan" source "drivers/watchdog/Kconfig.andes_atcwdt200" +source "drivers/watchdog/Kconfig.nxp_fs26" + endif # WATCHDOG diff --git a/drivers/watchdog/Kconfig.nxp_fs26 b/drivers/watchdog/Kconfig.nxp_fs26 new file mode 100644 index 00000000000..933cd96530f --- /dev/null +++ b/drivers/watchdog/Kconfig.nxp_fs26 @@ -0,0 +1,76 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig WDT_NXP_FS26 + bool "NXP FS26 SBC watchdog driver" + default y + depends on DT_HAS_NXP_FS26_WDOG_ENABLED + select SPI + select GPIO + help + Enable the NXP FS26 SBC watchdog driver. + +if WDT_NXP_FS26 + +config WDT_NXP_FS26_INIT_PRIORITY + int + default 80 + help + Device driver initialization priority. Device is connected to SPI bus, + so it has to be initialized after SPI driver. + +config WDT_NXP_FS26_ERROR_COUNTER_LIMIT + int "Watchdog error counter limit" + default 6 + help + Sets the maximum value of the watchdog error counter. Each time a + watchdog failure occurs, the device increments this counter by 2. The + watchdog error counter is decremented by 1 each time the watchdog is + properly refreshed. + + Possible values are 2, 4, 6, 8. + +config WDT_NXP_FS26_REFRESH_COUNTER_LIMIT + int "Watchdog refresh counter limit" + default 6 + help + Sets the maximum value of the watchdog refresh counter. Each time the + watchdog is properly refreshed, this counter is incremented by 1. Each + time this counter reaches its maximum value and if the next refresh is + also good, the fault error counter is decremented by 1. Each time + there is a bad watchdog refresh, this counter is reset to 0. + + Possible values are 1, 2, 4, 6. + +config WDT_NXP_FS26_SEED + hex "Watchdog seed" + default 0x5ab2 + range 0x0 0xffff + help + Seed to pass to the device. This property can be used with both simple + and challenger watchdog configurations. In simple watchdog + configuration, values 0xffff and 0x0000 are not allowed. In challenger + watchdog configuration, value 0x0000 is not allowed. + +config WDT_NXP_FS26_EXIT_DEBUG_MODE + bool "Exit DEBUG mode" + help + If the device is started in DEBUG mode, the driver will exit this mode + so that the watchdog is enabled. Otherwise, if the device is in DEBUG + mode and this driver is enabled, it will fail to initialize. + +config WDT_NXP_FS26_INT_THREAD_STACK_SIZE + int "Stack size for internal interrupt handler" + default 1024 + help + Size of the stack used for internal thread which is ran for + interrupt processing. + +config WDT_NXP_FS26_INT_THREAD_PRIO + int "Priority for internal incoming packet handler" + default 2 + help + Priority level for internal cooperative thread which is ran for + interrupt processing. + +endif # WDT_NXP_FS26 diff --git a/drivers/watchdog/wdt_nxp_fs26.c b/drivers/watchdog/wdt_nxp_fs26.c new file mode 100644 index 00000000000..07cddc1bd3b --- /dev/null +++ b/drivers/watchdog/wdt_nxp_fs26.c @@ -0,0 +1,838 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_fs26_wdog + +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL +#include +LOG_MODULE_REGISTER(wdt_nxp_fs26); + +#include "wdt_nxp_fs26.h" + +#if defined(CONFIG_BIG_ENDIAN) +#define SWAP_ENDIANNESS +#endif + +#define FS26_CRC_TABLE_SIZE 256U +#define FS26_CRC_INIT 0xff +#define FS26_FS_WD_TOKEN_DEFAULT 0x5ab2 +#define FS26_INIT_FS_TIMEOUT_MS 1000U + +/* Helper macros to set register values from Kconfig options */ +#define WD_ERR_LIMIT(x) CONCAT(WD_ERR_LIMIT_, x) +#define WD_RFR_LIMIT(x) CONCAT(WD_RFR_LIMIT_, x) +#define WDW_PERIOD(x) CONCAT(CONCAT(WDW_PERIOD_, x), MS) + +#define BAD_WD_REFRESH_ERROR_STRING(x) \ + ((((x) & BAD_WD_DATA) ? "error in the data" : \ + (((x) & BAD_WD_TIMING) ? "error in the timing (window)" \ + : "unknown error"))) + +enum fs26_wd_type { + FS26_WD_SIMPLE, + FS26_WD_CHALLENGER +}; + +struct fs26_spi_rx_frame { + union { + struct { + uint8_t m_aval : 1; + uint8_t fs_en : 1; + uint8_t fs_g : 1; + uint8_t com_g : 1; + uint8_t wio_g : 1; + uint8_t vsup_g : 1; + uint8_t reg_g : 1; + uint8_t tsd_g : 1; + }; + uint8_t raw; + } status; + uint16_t data; +}; + +struct fs26_spi_tx_frame { + bool write; + uint8_t addr; + uint16_t data; +}; + +struct wdt_nxp_fs26_config { + struct spi_dt_spec spi; + enum fs26_wd_type wd_type; + struct gpio_dt_spec int_gpio; +}; + +struct wdt_nxp_fs26_data { + wdt_callback_t callback; + uint16_t token; /* local copy of the watchdog token */ + bool timeout_installed; + uint8_t window_period; + uint8_t window_duty_cycle; + uint8_t fs_reaction; + struct gpio_callback int_gpio_cb; + struct k_sem int_sem; + struct k_thread int_thread; + + K_KERNEL_STACK_MEMBER(int_thread_stack, CONFIG_WDT_NXP_FS26_INT_THREAD_STACK_SIZE); +}; + +/* + * Allowed values for watchdog period and duty cycle (CLOSED window). + * The index is the value to write to the register. Keep values in ascending order. + */ +static const uint32_t fs26_period_values[] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64, 128, 256, 512, 1024 +}; + +static const double fs26_dc_closed_values[] = { + 0.3125, 0.375, 0.5, 0.625, 0.6875, 0.75, 0.8125 +}; + +/* CRC lookup table */ +static const uint8_t FS26_CRC_TABLE[FS26_CRC_TABLE_SIZE] = { + 0x00u, 0x1du, 0x3au, 0x27u, 0x74u, 0x69u, 0x4eu, 0x53u, 0xe8u, + 0xf5u, 0xd2u, 0xcfu, 0x9cu, 0x81u, 0xa6u, 0xbbu, 0xcdu, 0xd0u, + 0xf7u, 0xeau, 0xb9u, 0xa4u, 0x83u, 0x9eu, 0x25u, 0x38u, 0x1fu, + 0x02u, 0x51u, 0x4cu, 0x6bu, 0x76u, 0x87u, 0x9au, 0xbdu, 0xa0u, + 0xf3u, 0xeeu, 0xc9u, 0xd4u, 0x6fu, 0x72u, 0x55u, 0x48u, 0x1bu, + 0x06u, 0x21u, 0x3cu, 0x4au, 0x57u, 0x70u, 0x6du, 0x3eu, 0x23u, + 0x04u, 0x19u, 0xa2u, 0xbfu, 0x98u, 0x85u, 0xd6u, 0xcbu, 0xecu, + 0xf1u, 0x13u, 0x0eu, 0x29u, 0x34u, 0x67u, 0x7au, 0x5du, 0x40u, + 0xfbu, 0xe6u, 0xc1u, 0xdcu, 0x8fu, 0x92u, 0xb5u, 0xa8u, 0xdeu, + 0xc3u, 0xe4u, 0xf9u, 0xaau, 0xb7u, 0x90u, 0x8du, 0x36u, 0x2bu, + 0x0cu, 0x11u, 0x42u, 0x5fu, 0x78u, 0x65u, 0x94u, 0x89u, 0xaeu, + 0xb3u, 0xe0u, 0xfdu, 0xdau, 0xc7u, 0x7cu, 0x61u, 0x46u, 0x5bu, + 0x08u, 0x15u, 0x32u, 0x2fu, 0x59u, 0x44u, 0x63u, 0x7eu, 0x2du, + 0x30u, 0x17u, 0x0au, 0xb1u, 0xacu, 0x8bu, 0x96u, 0xc5u, 0xd8u, + 0xffu, 0xe2u, 0x26u, 0x3bu, 0x1cu, 0x01u, 0x52u, 0x4fu, 0x68u, + 0x75u, 0xceu, 0xd3u, 0xf4u, 0xe9u, 0xbau, 0xa7u, 0x80u, 0x9du, + 0xebu, 0xf6u, 0xd1u, 0xccu, 0x9fu, 0x82u, 0xa5u, 0xb8u, 0x03u, + 0x1eu, 0x39u, 0x24u, 0x77u, 0x6au, 0x4du, 0x50u, 0xa1u, 0xbcu, + 0x9bu, 0x86u, 0xd5u, 0xc8u, 0xefu, 0xf2u, 0x49u, 0x54u, 0x73u, + 0x6eu, 0x3du, 0x20u, 0x07u, 0x1au, 0x6cu, 0x71u, 0x56u, 0x4bu, + 0x18u, 0x05u, 0x22u, 0x3fu, 0x84u, 0x99u, 0xbeu, 0xa3u, 0xf0u, + 0xedu, 0xcau, 0xd7u, 0x35u, 0x28u, 0x0fu, 0x12u, 0x41u, 0x5cu, + 0x7bu, 0x66u, 0xddu, 0xc0u, 0xe7u, 0xfau, 0xa9u, 0xb4u, 0x93u, + 0x8eu, 0xf8u, 0xe5u, 0xc2u, 0xdfu, 0x8cu, 0x91u, 0xb6u, 0xabu, + 0x10u, 0x0du, 0x2au, 0x37u, 0x64u, 0x79u, 0x5eu, 0x43u, 0xb2u, + 0xafu, 0x88u, 0x95u, 0xc6u, 0xdbu, 0xfcu, 0xe1u, 0x5au, 0x47u, + 0x60u, 0x7du, 0x2eu, 0x33u, 0x14u, 0x09u, 0x7fu, 0x62u, 0x45u, + 0x58u, 0x0bu, 0x16u, 0x31u, 0x2cu, 0x97u, 0x8au, 0xadu, 0xb0u, + 0xe3u, 0xfeu, 0xd9u, 0xc4u +}; + +static uint8_t fs26_calcrc(const uint8_t *data, size_t size) +{ + uint8_t crc; + uint8_t tableidx; + uint8_t i; + + /* Set CRC token value */ + crc = FS26_CRC_INIT; + + for (i = size; i > 0; i--) { + tableidx = crc ^ data[i]; + crc = FS26_CRC_TABLE[tableidx]; + } + + return crc; +} + +static int fs26_spi_transceive(const struct spi_dt_spec *spi, + struct fs26_spi_tx_frame *tx_frame, + struct fs26_spi_rx_frame *rx_frame) +{ + uint32_t tx_buf; + uint32_t rx_buf; + uint8_t crc; + int retval; + + struct spi_buf spi_tx_buf = { + .buf = &tx_buf, + .len = sizeof(tx_buf) + }; + struct spi_buf spi_rx_buf = { + .buf = &rx_buf, + .len = sizeof(rx_buf) + }; + struct spi_buf_set spi_tx_set = { + .buffers = &spi_tx_buf, + .count = 1U + }; + struct spi_buf_set spi_rx_set = { + .buffers = &spi_rx_buf, + .count = 1U + }; + + /* Create frame to Tx, always for Fail Safe */ + tx_buf = (uint32_t)(FS26_SET_REG_ADDR(tx_frame->addr) + | FS26_SET_DATA(tx_frame->data) + | (tx_frame->write ? FS26_RW : 0)); + + crc = fs26_calcrc((uint8_t *)&tx_buf, sizeof(tx_buf) - 1); + tx_buf |= (uint32_t)FS26_SET_CRC(crc); + +#if defined(SWAP_ENDIANNESS) + tx_buf = __builtin_bswap32(tx_buf); +#endif + + retval = spi_transceive_dt(spi, &spi_tx_set, &spi_rx_set); + if (retval) { + goto error; + } + +#if defined(SWAP_ENDIANNESS) + rx_buf = __builtin_bswap32(rx_buf); +#endif + + /* Verify CRC of Rx frame */ + crc = fs26_calcrc((uint8_t *)&rx_buf, sizeof(rx_buf) - 1); + if (crc != ((uint8_t)FS26_GET_CRC(rx_buf))) { + LOG_ERR("Rx invalid CRC"); + retval = -EIO; + goto error; + } + + if (rx_frame) { + rx_frame->status.raw = (uint8_t)FS26_GET_DEV_STATUS(rx_buf); + rx_frame->data = (uint16_t)FS26_GET_DATA(rx_buf); + } + +error: + return retval; +} + +/** + * @brief Get value of register with address @p addr + * + * @param spi SPI specs for interacting with the device + * @param addr Register address + * @param rx_frame SPI frame containing read data and device status flags + * + * @return 0 on success, error code otherwise + */ +static int fs26_getreg(const struct spi_dt_spec *spi, uint8_t addr, + struct fs26_spi_rx_frame *rx_frame) +{ + struct fs26_spi_tx_frame tx_frame = { + .addr = addr, + .write = 0, + .data = 0 + }; + + return fs26_spi_transceive(spi, &tx_frame, rx_frame); +} + +/** + * @brief Set @p regval value in register with address @p addr + * + * @param spi SPI specs for interacting with the device + * @param addr Register address + * @param regval Register value to set + * + * @return 0 on success, error code otherwise + */ +static int fs26_setreg(const struct spi_dt_spec *spi, uint8_t addr, uint16_t regval) +{ + struct fs26_spi_tx_frame tx_frame = { + .addr = addr, + .write = true, + .data = regval + }; + + return fs26_spi_transceive(spi, &tx_frame, NULL); +} + +/** + * @brief Calculate watchdog answer based on received token + * + * @return answer value to write to FS_WD_ANSWER + */ +static inline uint16_t fs26_wd_compute_answer(uint16_t token) +{ + uint32_t tmp = token; + + tmp *= 4U; + tmp += 6U; + tmp -= 4U; + tmp = ~tmp; + tmp /= 4U; + + return (uint16_t)tmp; +} + +/** + * @brief Refresh the watchdog and verify the refresh was good. + * + * @return 0 on success, error code otherwise + */ +static int fs26_wd_refresh(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct wdt_nxp_fs26_data *data = dev->data; + int retval = 0; + int key; + uint16_t answer; + struct fs26_spi_rx_frame rx_frame; + + if (config->wd_type == FS26_WD_SIMPLE) { + if (fs26_setreg(&config->spi, FS26_FS_WD_ANSWER, data->token) == 0) { + LOG_ERR("Failed to write answer"); + retval = -EIO; + } + } else if (config->wd_type == FS26_WD_CHALLENGER) { + key = irq_lock(); + + /* Read challenge token generated by the device */ + if (fs26_getreg(&config->spi, FS26_FS_WD_TOKEN, &rx_frame)) { + LOG_ERR("Failed to obtain watchdog token"); + retval = -EIO; + } else { + data->token = rx_frame.data; + LOG_DBG("Watchdog token is %x", data->token); + + answer = fs26_wd_compute_answer(data->token); + if (fs26_setreg(&config->spi, FS26_FS_WD_ANSWER, answer)) { + LOG_ERR("Failed to write answer"); + retval = -EIO; + } + } + + irq_unlock(key); + } else { + retval = -EINVAL; + } + + /* Check if watchdog refresh was successful */ + if (!retval) { + if (!fs26_getreg(&config->spi, FS26_FS_GRL_FLAGS, &rx_frame)) { + if ((rx_frame.data & FS_WD_G_MASK) == FS_WD_G) { + if (!fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame)) { + LOG_ERR("Bad watchdog refresh, %s", + BAD_WD_REFRESH_ERROR_STRING(rx_frame.data)); + } + retval = -EIO; + } else { + LOG_DBG("Refreshed the watchdog"); + } + } + } + + return retval; +} + +/** + * @brief Wait for state machine to be at in INIT_FS state + * + * @return 0 on success, -ETIMEDOUT if timedout + */ +static int fs26_poll_for_init_fs_state(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct fs26_spi_rx_frame rx_frame; + uint32_t regval = 0; + int64_t timeout; + int64_t now; + + timeout = k_uptime_get() + FS26_INIT_FS_TIMEOUT_MS; + + do { + if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) { + regval = rx_frame.data; + } + k_sleep(K_MSEC(1)); + now = k_uptime_get(); + } while ((now < timeout) && (regval & FS_STATES_MASK) != FS_STATES_INIT_FS); + + if (now >= timeout) { + LOG_ERR("Timedout waiting for INIT_FS state"); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * @brief Go to INIT_FS state from any FS state after INIT_FS + * + * After INIT_FS closure, it is possible to come back to INIT_FS with the + * GOTO_INIT bit in FS_SAFE_IOS_1 register from any FS state after INIT_FS. + * + * @return 0 on success, error code otherwise + */ +static int fs26_goto_init_fs_state(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct fs26_spi_rx_frame rx_frame; + uint32_t current_state; + int retval = -EIO; + + if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) { + current_state = rx_frame.data & FS_STATES_MASK; + if (current_state < FS_STATES_INIT_FS) { + LOG_ERR("Cannot go to INIT_FS from current state %x", current_state); + retval = -EIO; + } else if (current_state == FS_STATES_INIT_FS) { + retval = 0; + } else { + fs26_setreg(&config->spi, FS26_FS_SAFE_IOS_1, (uint32_t)FS_GOTO_INIT); + retval = fs26_poll_for_init_fs_state(dev); + } + } + + return retval; +} + +/** + * @brief Close INIT_FS phase with a (good) watchdog refresh. + * + * @return 0 on success, error code otherwise + */ +static inline int fs26_exit_init_fs_state(const struct device *dev) +{ + return fs26_wd_refresh(dev); +} + +static int wdt_nxp_fs26_feed(const struct device *dev, int channel_id) +{ + struct wdt_nxp_fs26_data *data = dev->data; + + if (channel_id != 0) { + LOG_ERR("Invalid channel ID"); + return -EINVAL; + } + + if (!data->timeout_installed) { + LOG_ERR("No timeout installed"); + return -EINVAL; + } + + return fs26_wd_refresh(dev); +} + +static int wdt_nxp_fs26_setup(const struct device *dev, uint8_t options) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct wdt_nxp_fs26_data *data = dev->data; + uint32_t regval; + + if (!data->timeout_installed) { + LOG_ERR("No timeout installed"); + return -EINVAL; + } + + if ((options & WDT_OPT_PAUSE_IN_SLEEP) || (options & WDT_OPT_PAUSE_HALTED_BY_DBG)) { + return -ENOTSUP; + } + + /* + * Apply fail-safe reaction configuration on RSTB and/or the safety output(s), + * configurable during the initialization phase. + */ + if (fs26_goto_init_fs_state(dev)) { + LOG_ERR("Failed to go to INIT_FS"); + return -EIO; + } + + regval = WD_ERR_LIMIT(CONFIG_WDT_NXP_FS26_ERROR_COUNTER_LIMIT) + | WD_RFR_LIMIT(CONFIG_WDT_NXP_FS26_REFRESH_COUNTER_LIMIT) + | ((data->fs_reaction << WD_FS_REACTION_SHIFT) & WD_FS_REACTION_MASK); + + fs26_setreg(&config->spi, FS26_FS_I_WD_CFG, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_WD_CFG, ~regval); + + /* Apply watchdog window configuration, configurable during any FS state */ + regval = ((data->window_period << WDW_PERIOD_SHIFT) & WDW_PERIOD_MASK) + | ((data->window_duty_cycle << WDW_DC_SHIFT) & WDW_DC_MASK) + | WDW_RECOVERY_DISABLE; + + fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval); + fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval); + + /* + * The new watchdog window is effective after the next watchdog refresh, + * so feed the watchdog once to make it effective after exiting this + * function. Also it's required to close init phase. + */ + if (fs26_exit_init_fs_state(dev)) { + LOG_ERR("Failed to close INIT_FS"); + return -EIO; + } + + return 0; +} + +static int wdt_nxp_fs26_install_timeout(const struct device *dev, + const struct wdt_timeout_cfg *cfg) +{ + struct wdt_nxp_fs26_data *data = dev->data; + uint32_t window_min; + uint8_t i; + + if (data->timeout_installed) { + LOG_ERR("No more timeouts can be installed"); + return -ENOMEM; + } + + if ((cfg->window.max == 0) || (cfg->window.max > 1024) + || (cfg->window.max <= cfg->window.min)) { + LOG_ERR("Invalid timeout value"); + return -EINVAL; + } + + /* Find nearest period value (rounded up) */ + for (i = 0; i < ARRAY_SIZE(fs26_period_values); i++) { + if (fs26_period_values[i] >= cfg->window.max) { + break; + } + } + data->window_period = i; + LOG_DBG("window.max requested %d ms, using %d ms", + cfg->window.max, fs26_period_values[data->window_period]); + + /* + * Find nearest duty cycle value based on new period, that results in a + * window's minimum near the requested (rounded up) + */ + for (i = 0; i < ARRAY_SIZE(fs26_dc_closed_values); i++) { + window_min = (uint32_t)(fs26_dc_closed_values[i] + * fs26_period_values[data->window_period]); + if (window_min >= cfg->window.min) { + break; + } + } + if (i >= ARRAY_SIZE(fs26_dc_closed_values)) { + LOG_ERR("Watchdog opened window too small"); + return -EINVAL; + } + data->window_duty_cycle = i; + + LOG_DBG("window.min requested %d ms, using %d ms (%.2f%%)", + cfg->window.min, window_min, + fs26_dc_closed_values[data->window_duty_cycle] * 100); + + /* Fail-safe reaction configuration */ + switch (cfg->flags) { + case WDT_FLAG_RESET_SOC: + __fallthrough; + case WDT_FLAG_RESET_CPU_CORE: + data->fs_reaction = WD_FS_REACTION_RSTB_FS0B >> WD_FS_REACTION_SHIFT; + LOG_DBG("Configuring reset mode"); + break; + case WDT_FLAG_RESET_NONE: + data->fs_reaction = WD_FS_REACTION_NO_ACTION >> WD_FS_REACTION_SHIFT; + LOG_DBG("Configuring non-reset mode"); + break; + default: + LOG_ERR("Unsupported watchdog configuration flag"); + return -EINVAL; + } + + data->callback = cfg->callback; + data->timeout_installed = true; + + /* Always return channel ID equal to 0 */ + return 0; +} + +static int wdt_nxp_fs26_disable(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct wdt_nxp_fs26_data *data = dev->data; + struct fs26_spi_rx_frame rx_frame; + uint32_t regval; + + if (fs26_getreg(&config->spi, FS26_FS_WDW_DURATION, &rx_frame)) { + return -EIO; + } + if ((rx_frame.data & WDW_PERIOD_MASK) == WDW_PERIOD_DISABLE) { + LOG_ERR("Watchdog already disabled"); + return -EFAULT; + } + + /* The watchdog window can be disabled only during the initialization phase */ + if (fs26_goto_init_fs_state(dev)) { + LOG_ERR("Failed to go to INIT_FS"); + return -EIO; + } + + regval = WDW_PERIOD_DISABLE | WDW_RECOVERY_DISABLE; + fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval); + fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval); + + /* The watchdog disabling is effective when the initialization phase is closed */ + if (fs26_exit_init_fs_state(dev)) { + LOG_ERR("Failed to close INIT_FS"); + return -EIO; + } + + LOG_DBG("Watchdog disabled"); + data->timeout_installed = false; + + return 0; +} + +static void wdt_nxp_fs26_int_thread(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct wdt_nxp_fs26_data *data = dev->data; + struct fs26_spi_rx_frame rx_frame; + uint32_t regval; + + while (1) { + k_sem_take(&data->int_sem, K_FOREVER); + + if ((!fs26_getreg(&config->spi, FS26_FS_GRL_FLAGS, &rx_frame)) + && ((rx_frame.data & FS_WD_G_MASK) == FS_WD_G)) { + + if ((!fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame)) + && (rx_frame.data & BAD_WD_TIMING)) { + + /* Clear flag */ + regval = BAD_WD_TIMING; + fs26_setreg(&config->spi, FS26_FS_DIAG_SAFETY1, regval); + + /* Invoke user callback */ + if (data->callback && data->timeout_installed) { + data->callback(dev, 0); + } + } + } + } +} + +static void wdt_nxp_fs26_int_callback(const struct device *dev, + struct gpio_callback *cb, + uint32_t pins) +{ + struct wdt_nxp_fs26_data *data = CONTAINER_OF(cb, struct wdt_nxp_fs26_data, + int_gpio_cb); + + ARG_UNUSED(dev); + ARG_UNUSED(pins); + + k_sem_give(&data->int_sem); +} + +static int wdt_nxp_fs26_init(const struct device *dev) +{ + const struct wdt_nxp_fs26_config *config = dev->config; + struct wdt_nxp_fs26_data *data = dev->data; + struct fs26_spi_rx_frame rx_frame; + uint32_t regval; + + /* Validate bus is ready */ + if (!spi_is_ready_dt(&config->spi)) { + return -ENODEV; + } + + k_sem_init(&data->int_sem, 0, 1); + + /* Configure GPIO used for INTB signal */ + if (!gpio_is_ready_dt(&config->int_gpio)) { + LOG_ERR("GPIO port %s not ready", config->int_gpio.port->name); + return -ENODEV; + } + + if (gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT)) { + LOG_ERR("Unable to configure GPIO pin %u", config->int_gpio.pin); + return -EIO; + } + + gpio_init_callback(&(data->int_gpio_cb), wdt_nxp_fs26_int_callback, + BIT(config->int_gpio.pin)); + + if (gpio_add_callback(config->int_gpio.port, &(data->int_gpio_cb))) { + return -EINVAL; + } + + if (gpio_pin_interrupt_configure_dt(&config->int_gpio, + GPIO_INT_EDGE_FALLING)) { + return -EINVAL; + } + + k_thread_create(&data->int_thread, data->int_thread_stack, + CONFIG_WDT_NXP_FS26_INT_THREAD_STACK_SIZE, + (k_thread_entry_t)wdt_nxp_fs26_int_thread, + (void *)dev, NULL, NULL, + K_PRIO_COOP(CONFIG_WDT_NXP_FS26_INT_THREAD_PRIO), + 0, K_NO_WAIT); + + /* Verify FS BIST before proceeding */ + if (fs26_getreg(&config->spi, FS26_FS_DIAG_SAFETY1, &rx_frame)) { + return -EIO; + } + + if ((rx_frame.data & (ABIST1_PASS_MASK | LBIST_STATUS_MASK)) + != (ABIST1_PASS | LBIST_STATUS_OK)) { + + LOG_ERR("BIST failed 0x%x", rx_frame.data); + return -EIO; + } + + /* Get FS state machine state */ + if (fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) { + return -EIO; + } + + /* Verify if in DEBUG mode */ + if ((rx_frame.data & DBG_MODE_MASK) == DBG_MODE) { + if (IS_ENABLED(CONFIG_WDT_NXP_FS26_EXIT_DEBUG_MODE)) { + LOG_DBG("Exiting DEBUG mode"); + regval = rx_frame.data | EXIT_DBG_MODE; + fs26_setreg(&config->spi, FS26_FS_STATES, regval); + } else { + LOG_ERR("In DEBUG mode, watchdog is disabled"); + return -EIO; + } + } + + /* Go to INIT_FS state, if not already there */ + if (fs26_goto_init_fs_state(dev)) { + LOG_ERR("Failed to go to INIT_FS"); + return -EIO; + } + + /* Clear pending FS diagnostic flags before initializing */ + regval = BAD_WD_DATA | BAD_WD_TIMING | ABIST2_PASS | ABIST2_DONE + | SPI_FS_CLK | SPI_FS_REQ | SPI_FS_CRC | FS_OSC_DRIFT; + fs26_setreg(&config->spi, FS26_FS_DIAG_SAFETY1, regval); + + /* + * Perform the following sequence for all INIT_FS registers (FS_I_xxxx) + * - Write the desired data in the FS_I_Register_A (data) + * - Write the opposite in the FS_I_NOT_Register_A (~data) + */ + + /* OVUV_SAFE_REACTION1 */ + regval = VMON_PRE_OV_FS_REACTION_NO_EFFECT | + VMON_PRE_UV_FS_REACTION_NO_EFFECT | + VMON_CORE_OV_FS_REACTION_NO_EFFECT | + VMON_CORE_UV_FS_REACTION_NO_EFFECT | + VMON_LDO1_OV_FS_REACTION_NO_EFFECT | + VMON_LDO1_UV_FS_REACTION_NO_EFFECT | + VMON_LDO2_OV_FS_REACTION_NO_EFFECT | + VMON_LDO2_UV_FS_REACTION_NO_EFFECT; + + fs26_setreg(&config->spi, FS26_FS_I_OVUV_SAFE_REACTION1, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_OVUV_SAFE_REACTION1, ~regval); + + /* OVUV_SAFE_REACTION2 */ + regval = VMON_EXT_OV_FS_REACTION_NO_EFFECT | + VMON_EXT_UV_FS_REACTION_NO_EFFECT | + VMON_REF_OV_FS_REACTION_NO_EFFECT | + VMON_REF_UV_FS_REACTION_NO_EFFECT | + VMON_TRK2_OV_FS_REACTION_NO_EFFECT | + VMON_TRK2_UV_FS_REACTION_NO_EFFECT | + VMON_TRK1_OV_FS_REACTION_NO_EFFECT | + VMON_TRK1_UV_FS_REACTION_NO_EFFECT; + + fs26_setreg(&config->spi, FS26_FS_I_OVUV_SAFE_REACTION2, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_OVUV_SAFE_REACTION2, ~regval); + + /* FS_I_SAFE_INPUTS */ + regval = FCCU_CFG_NO_MONITORING | ERRMON_ACK_TIME_32MS; + + fs26_setreg(&config->spi, FS26_FS_I_SAFE_INPUTS, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_SAFE_INPUTS, ~regval); + + /* FS_I_FSSM */ + regval = FLT_ERR_REACTION_NO_EFFECT | CLK_MON_DIS | DIS8S; + + fs26_setreg(&config->spi, FS26_FS_I_FSSM, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_FSSM, ~regval); + + /* FS_I_WD_CFG */ + regval = WD_ERR_LIMIT(CONFIG_WDT_NXP_FS26_ERROR_COUNTER_LIMIT) + | WD_RFR_LIMIT(CONFIG_WDT_NXP_FS26_REFRESH_COUNTER_LIMIT) + | WD_FS_REACTION_NO_ACTION; + + fs26_setreg(&config->spi, FS26_FS_I_WD_CFG, regval); + fs26_setreg(&config->spi, FS26_FS_I_NOT_WD_CFG, ~regval); + + /* FS_WDW_DURATION */ + /* Watchdog always disabled at boot */ + regval = WDW_PERIOD_DISABLE | WDW_RECOVERY_DISABLE; + + fs26_setreg(&config->spi, FS26_FS_WDW_DURATION, regval); + fs26_setreg(&config->spi, FS26_FS_NOT_WDW_DURATION, ~regval); + + /* Set watchdog seed if not using the default */ + if (data->token != FS26_FS_WD_TOKEN_DEFAULT) { + LOG_DBG("Set seed to %x", data->token); + fs26_setreg(&config->spi, FS26_FS_WD_TOKEN, data->token); + } + + /* Mask all Fail-Safe interrupt sources except for watchdog bad refresh */ + regval = ~BAD_WD_M; + fs26_setreg(&config->spi, FS26_FS_INTB_MASK, regval); + + /* Mask all main interrupt souces */ + regval = 0xffff; + fs26_setreg(&config->spi, FS26_M_TSD_MSK, regval); + fs26_setreg(&config->spi, FS26_M_REG_MSK, regval); + fs26_setreg(&config->spi, FS26_M_VSUP_MSK, regval); + fs26_setreg(&config->spi, FS26_M_WIO_MSK, regval); + fs26_setreg(&config->spi, FS26_M_COM_MSK, regval); + + /* INIT_FS must be closed before the 256 ms timeout */ + if (fs26_exit_init_fs_state(dev)) { + LOG_ERR("Failed to close INIT_FS"); + return -EIO; + } + + /* After INIT_FS is completed, check for data corruption in init registers */ + if (!fs26_getreg(&config->spi, FS26_FS_STATES, &rx_frame)) { + if ((rx_frame.data & REG_CORRUPT_MASK) == REG_CORRUPT) { + LOG_ERR("Data content corruption detected in init registers"); + return -EIO; + } + } + + return 0; +} + +static const struct wdt_driver_api wdt_nxp_fs26_api = { + .setup = wdt_nxp_fs26_setup, + .disable = wdt_nxp_fs26_disable, + .install_timeout = wdt_nxp_fs26_install_timeout, + .feed = wdt_nxp_fs26_feed, +}; + +#define FS26_WDT_DEVICE_INIT(n) \ + COND_CODE_1(DT_INST_ENUM_IDX(n, type), \ + (BUILD_ASSERT(CONFIG_WDT_NXP_FS26_SEED != 0x0, \ + "Seed value 0x0000 is not allowed");), \ + (BUILD_ASSERT((CONFIG_WDT_NXP_FS26_SEED != 0x0) \ + && (CONFIG_WDT_NXP_FS26_SEED != 0xffff), \ + "Seed values 0x0000 and 0xffff are not allowed");)) \ + \ + static struct wdt_nxp_fs26_data wdt_nxp_fs26_data_##n = { \ + .token = CONFIG_WDT_NXP_FS26_SEED, \ + }; \ + \ + static const struct wdt_nxp_fs26_config wdt_nxp_fs26_config_##n = { \ + .spi = SPI_DT_SPEC_INST_GET(n, \ + SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(32), 0), \ + .wd_type = CONCAT(FS26_WD_, DT_INST_STRING_UPPER_TOKEN(n, type)), \ + .int_gpio = GPIO_DT_SPEC_INST_GET(n, int_gpios), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + wdt_nxp_fs26_init, \ + NULL, \ + &wdt_nxp_fs26_data_##n, \ + &wdt_nxp_fs26_config_##n, \ + POST_KERNEL, \ + CONFIG_WDT_NXP_FS26_INIT_PRIORITY, \ + &wdt_nxp_fs26_api); + +DT_INST_FOREACH_STATUS_OKAY(FS26_WDT_DEVICE_INIT) diff --git a/drivers/watchdog/wdt_nxp_fs26.h b/drivers/watchdog/wdt_nxp_fs26.h new file mode 100644 index 00000000000..25787ba355c --- /dev/null +++ b/drivers/watchdog/wdt_nxp_fs26.h @@ -0,0 +1,580 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_WATCHDOG_WDT_NXP_FS26_H_ +#define ZEPHYR_DRIVERS_WATCHDOG_WDT_NXP_FS26_H_ + +/* FS26 SPI Tx frame fields */ + +/* Main or Fail-safe register selection (M/FS) */ +#define FS26_M_FS (0x1 << 31) +/* Register Address + M/FS */ +#define FS26_REG_ADDR_SHIFT (25) +#define FS26_REG_ADDR_MASK (0x7f << FS26_REG_ADDR_SHIFT) +#define FS26_SET_REG_ADDR(n) (((n) << FS26_REG_ADDR_SHIFT) & FS26_REG_ADDR_MASK) +#define FS26_GET_REG_ADDR(n) (((n) & FS26_REG_ADDR_MASK) >> FS26_REG_ADDR_SHIFT) +/* Read/Write (reading = 0) */ +#define FS26_RW (0x1 << 24) + +/* FS26 SPI Rx frame fields */ + +/* Device status flags */ +#define FS26_DEV_STATUS_SHIFT (24) +#define FS26_DEV_STATUS_MASK (0xff << FS26_DEV_STATUS_SHIFT) +#define FS26_GET_DEV_STATUS(n) (((n) << FS26_DEV_STATUS_SHIFT) & FS26_DEV_STATUS_MASK) +/* Main State machine availability (M_AVAL) */ +#define FS26_M_AVAL (0x1 << 31) +/* Fail Safe State machine status (FS_EN) */ +#define FS26_FS_EN (0x1 << 30) +/* Interrupt notification from the Fail-Safe domain */ +#define FS26_FS_G (0x1 << 29) +/* Interrupt notification from the M_COM_FLG register */ +#define FS26_COM_G (0x1 << 28) +/* Interrupt notification from the M_WIO_FLG register */ +#define FS26_WIO_G (0x1 << 27) +/* Interrupt notification from the M_VSUP_FLG register */ +#define FS26_VSUP_G (0x1 << 26) +/* Interrupt notification from the M_REG_FLG register */ +#define FS26_REG_G (0x1 << 25) +/* Interrupt notification from the M_TSD_FLG register */ +#define FS26_TSD_G (0x1 << 24) + +/* FS26 SPI Tx/Rx frame common fields */ + +/* DATA_MSB */ +#define FS26_DATA_SHIFT (8) +#define FS26_DATA_MASK (0xffff << FS26_DATA_SHIFT) +#define FS26_SET_DATA(n) (((n) << FS26_DATA_SHIFT) & FS26_DATA_MASK) +#define FS26_GET_DATA(n) (((n) & FS26_DATA_MASK) >> FS26_DATA_SHIFT) +/* DATA_LSB */ +#define FS26_DATA_LSB_SHIFT (8) +#define FS26_DATA_LSB_MASK (0xff << FS26_DATA_LSB_SHIFT) +#define FS26_SET_DATA_LSB(n) (((n) << FS26_DATA_LSB_SHIFT) & FS26_DATA_LSB_MASK) +#define FS26_GET_DATA_LSB(n) (((n) & FS26_DATA_LSB_MASK) >> FS26_DATA_LSB_SHIFT) +/* DATA_MSB */ +#define FS26_DATA_MSB_SHIFT (16) +#define FS26_DATA_MSB_MASK (0xff << FS26_DATA_MSB_SHIFT) +#define FS26_SET_DATA_MSB(n) (((n) << FS26_DATA_MSB_SHIFT) & FS26_DATA_MSB_MASK) +#define FS26_GET_DATA_MSB(n) (((n) & FS26_DATA_MSB_MASK) >> FS26_DATA_MSB_SHIFT) +/* CRC */ +#define FS26_CRC_SHIFT (0) +#define FS26_CRC_MASK (0xff << FS26_CRC_SHIFT) +#define FS26_SET_CRC(n) (((n) << FS26_CRC_SHIFT) & FS26_CRC_MASK) +#define FS26_GET_CRC(n) (((n) & FS26_CRC_MASK) >> FS26_CRC_SHIFT) + +/* FS26 SPI register map */ + +#define FS26_M_DEVICE_ID (0x0) +#define FS26_M_PROGID (0x1) +#define FS26_M_STATUS (0x2) +#define FS26_M_TSD_FLG (0x3) +#define FS26_M_TSD_MSK (0x4) +#define FS26_M_REG_FLG (0x5) +#define FS26_M_REG_MSK (0x6) +#define FS26_M_VSUP_FLG (0x7) +#define FS26_M_VSUP_MSK (0x8) +#define FS26_M_WIO_FLG (0x9) +#define FS26_M_WIO_MSK (0xa) +#define FS26_M_COM_FLG (0xb) +#define FS26_M_COM_MSK (0xc) +#define FS26_M_SYS_CFG (0xd) +#define FS26_M_TSD_CFG (0xe) +#define FS26_M_REG_CFG (0xf) +#define FS26_M_WIO_CFG (0x10) +#define FS26_M_REG_CTRL1 (0x11) +#define FS26_M_REG_CTRL2 (0x12) +#define FS26_M_AMUX_CTRL (0x13) +#define FS26_M_LDT_CFG1 (0x14) +#define FS26_M_LDT_CFG2 (0x15) +#define FS26_M_LDT_CFG3 (0x16) +#define FS26_M_LDT_CTRL (0x17) +#define FS26_M_MEMORY0 (0x18) +#define FS26_M_MEMORY1 (0x19) + +/* FS26 Fail Safe register map */ + +#define FS26_FS_GRL_FLAGS (0x40) +#define FS26_FS_I_OVUV_SAFE_REACTION1 (0x41) +#define FS26_FS_I_NOT_OVUV_SAFE_REACTION1 (0x42) +#define FS26_FS_I_OVUV_SAFE_REACTION2 (0x43) +#define FS26_FS_I_NOT_OVUV_SAFE_REACTION2 (0x44) +#define FS26_FS_I_WD_CFG (0x45) +#define FS26_FS_I_NOT_WD_CFG (0x46) +#define FS26_FS_I_SAFE_INPUTS (0x47) +#define FS26_FS_I_NOT_SAFE_INPUTS (0x48) +#define FS26_FS_I_FSSM (0x49) +#define FS26_FS_I_NOT_FSSM (0x4a) +#define FS26_FS_WDW_DURATION (0x4b) +#define FS26_FS_NOT_WDW_DURATION (0x4c) +#define FS26_FS_WD_ANSWER (0x4d) +#define FS26_FS_WD_TOKEN (0x4e) +#define FS26_FS_ABIST_ON_DEMAND (0x4f) +#define FS26_FS_OVUV_REG_STATUS (0x50) +#define FS26_FS_RELEASE_FS0B_FS1B (0x51) +#define FS26_FS_SAFE_IOS_1 (0x52) +#define FS26_FS_SAFE_IOS_2 (0x53) +#define FS26_FS_DIAG_SAFETY1 (0x54) +#define FS26_FS_DIAG_SAFETY2 (0x55) +#define FS26_FS_INTB_MASK (0x56) +#define FS26_FS_STATES (0x57) +#define FS26_FS_LP_REQ (0x58) +#define FS26_FS_LDT_LPSEL (0x59) + +/* FS_I_OVUV_SAFE_REACTION1 register */ + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_PRE */ +#define VMON_PRE_OV_FS_REACTION_SHIFT (14) +#define VMON_PRE_OV_FS_REACTION_MASK (0x3 << VMON_PRE_OV_FS_REACTION_SHIFT) +#define VMON_PRE_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_PRE_OV_FS_REACTION_SHIFT) +#define VMON_PRE_OV_FS_REACTION_FS0B (0x1 << VMON_PRE_OV_FS_REACTION_SHIFT) +#define VMON_PRE_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_PRE_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_PRE */ +#define VMON_PRE_UV_FS_REACTION_SHIFT (12) +#define VMON_PRE_UV_FS_REACTION_MASK (0x3 << VMON_PRE_UV_FS_REACTION_SHIFT) +#define VMON_PRE_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_PRE_UV_FS_REACTION_SHIFT) +#define VMON_PRE_UV_FS_REACTION_FS0B (0x1 << VMON_PRE_UV_FS_REACTION_SHIFT) +#define VMON_PRE_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_PRE_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_CORE */ +#define VMON_CORE_OV_FS_REACTION_SHIFT (10) +#define VMON_CORE_OV_FS_REACTION_MASK (0x3 << VMON_CORE_OV_FS_REACTION_SHIFT) +#define VMON_CORE_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_CORE_OV_FS_REACTION_SHIFT) +#define VMON_CORE_OV_FS_REACTION_FS0B (0x1 << VMON_CORE_OV_FS_REACTION_SHIFT) +#define VMON_CORE_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_CORE_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_CORE */ +#define VMON_CORE_UV_FS_REACTION_SHIFT (8) +#define VMON_CORE_UV_FS_REACTION_MASK (0x3 << VMON_CORE_UV_FS_REACTION_SHIFT) +#define VMON_CORE_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_CORE_UV_FS_REACTION_SHIFT) +#define VMON_CORE_UV_FS_REACTION_FS0B (0x1 << VMON_CORE_UV_FS_REACTION_SHIFT) +#define VMON_CORE_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_CORE_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_LDO1 */ +#define VMON_LDO1_OV_FS_REACTION_SHIFT (6) +#define VMON_LDO1_OV_FS_REACTION_MASK (0x3 << VMON_LDO1_OV_FS_REACTION_SHIFT) +#define VMON_LDO1_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_LDO1_OV_FS_REACTION_SHIFT) +#define VMON_LDO1_OV_FS_REACTION_FS0B (0x1 << VMON_LDO1_OV_FS_REACTION_SHIFT) +#define VMON_LDO1_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_LDO1_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_LDO1 */ +#define VMON_LDO1_UV_FS_REACTION_SHIFT (4) +#define VMON_LDO1_UV_FS_REACTION_MASK (0x3 << VMON_LDO1_UV_FS_REACTION_SHIFT) +#define VMON_LDO1_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_LDO1_UV_FS_REACTION_SHIFT) +#define VMON_LDO1_UV_FS_REACTION_FS0B (0x1 << VMON_LDO1_UV_FS_REACTION_SHIFT) +#define VMON_LDO1_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_LDO1_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_LDO2 */ +#define VMON_LDO2_OV_FS_REACTION_SHIFT (2) +#define VMON_LDO2_OV_FS_REACTION_MASK (0x3 << VMON_LDO2_OV_FS_REACTION_SHIFT) +#define VMON_LDO2_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_LDO2_OV_FS_REACTION_SHIFT) +#define VMON_LDO2_OV_FS_REACTION_FS0B (0x1 << VMON_LDO2_OV_FS_REACTION_SHIFT) +#define VMON_LDO2_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_LDO2_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_LDO2 */ +#define VMON_LDO2_UV_FS_REACTION_SHIFT (0) +#define VMON_LDO2_UV_FS_REACTION_MASK (0x3 << VMON_LDO2_UV_FS_REACTION_SHIFT) +#define VMON_LDO2_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_LDO2_UV_FS_REACTION_SHIFT) +#define VMON_LDO2_UV_FS_REACTION_FS0B (0x1 << VMON_LDO2_UV_FS_REACTION_SHIFT) +#define VMON_LDO2_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_LDO2_UV_FS_REACTION_SHIFT) + +/* FS_I_OVUV_SAFE_REACTION2 register */ + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_EXT */ +#define VMON_EXT_OV_FS_REACTION_SHIFT (14) +#define VMON_EXT_OV_FS_REACTION_MASK (0x3 << VMON_EXT_OV_FS_REACTION_SHIFT) +#define VMON_EXT_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_EXT_OV_FS_REACTION_SHIFT) +#define VMON_EXT_OV_FS_REACTION_FS0B (0x1 << VMON_EXT_OV_FS_REACTION_SHIFT) +#define VMON_EXT_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_EXT_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_EXT */ +#define VMON_EXT_UV_FS_REACTION_SHIFT (12) +#define VMON_EXT_UV_FS_REACTION_MASK (0x3 << VMON_EXT_UV_FS_REACTION_SHIFT) +#define VMON_EXT_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_EXT_UV_FS_REACTION_SHIFT) +#define VMON_EXT_UV_FS_REACTION_FS0B (0x1 << VMON_EXT_UV_FS_REACTION_SHIFT) +#define VMON_EXT_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_EXT_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_REF */ +#define VMON_REF_OV_FS_REACTION_SHIFT (10) +#define VMON_REF_OV_FS_REACTION_MASK (0x3 << VMON_REF_OV_FS_REACTION_SHIFT) +#define VMON_REF_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_REF_OV_FS_REACTION_SHIFT) +#define VMON_REF_OV_FS_REACTION_FS0B (0x1 << VMON_REF_OV_FS_REACTION_SHIFT) +#define VMON_REF_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_REF_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_REF */ +#define VMON_REF_UV_FS_REACTION_SHIFT (8) +#define VMON_REF_UV_FS_REACTION_MASK (0x3 << VMON_REF_UV_FS_REACTION_SHIFT) +#define VMON_REF_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_REF_UV_FS_REACTION_SHIFT) +#define VMON_REF_UV_FS_REACTION_FS0B (0x1 << VMON_REF_UV_FS_REACTION_SHIFT) +#define VMON_REF_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_REF_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_TRK2 */ +#define VMON_TRK2_OV_FS_REACTION_SHIFT (6) +#define VMON_TRK2_OV_FS_REACTION_MASK (0x3 << VMON_TRK2_OV_FS_REACTION_SHIFT) +#define VMON_TRK2_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_TRK2_OV_FS_REACTION_SHIFT) +#define VMON_TRK2_OV_FS_REACTION_FS0B (0x1 << VMON_TRK2_OV_FS_REACTION_SHIFT) +#define VMON_TRK2_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_TRK2_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_TRK2 */ +#define VMON_TRK2_UV_FS_REACTION_SHIFT (4) +#define VMON_TRK2_UV_FS_REACTION_MASK (0x3 << VMON_TRK2_UV_FS_REACTION_SHIFT) +#define VMON_TRK2_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_TRK2_UV_FS_REACTION_SHIFT) +#define VMON_TRK2_UV_FS_REACTION_FS0B (0x1 << VMON_TRK2_UV_FS_REACTION_SHIFT) +#define VMON_TRK2_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_TRK2_UV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of OV detection on VMON_TRK1 */ +#define VMON_TRK1_OV_FS_REACTION_SHIFT (2) +#define VMON_TRK1_OV_FS_REACTION_MASK (0x3 << VMON_TRK1_OV_FS_REACTION_SHIFT) +#define VMON_TRK1_OV_FS_REACTION_NO_EFFECT (0x0 << VMON_TRK1_OV_FS_REACTION_SHIFT) +#define VMON_TRK1_OV_FS_REACTION_FS0B (0x1 << VMON_TRK1_OV_FS_REACTION_SHIFT) +#define VMON_TRK1_OV_FS_REACTION_RSTB_FS0B (0x2 << VMON_TRK1_OV_FS_REACTION_SHIFT) + +/* Reaction on RSTB or FAIL SAFE outputs in case of UV detection on VMON_TRK1 */ +#define VMON_TRK1_UV_FS_REACTION_SHIFT (0) +#define VMON_TRK1_UV_FS_REACTION_MASK (0x3 << VMON_TRK1_UV_FS_REACTION_SHIFT) +#define VMON_TRK1_UV_FS_REACTION_NO_EFFECT (0x0 << VMON_TRK1_UV_FS_REACTION_SHIFT) +#define VMON_TRK1_UV_FS_REACTION_FS0B (0x1 << VMON_TRK1_UV_FS_REACTION_SHIFT) +#define VMON_TRK1_UV_FS_REACTION_RSTB_FS0B (0x2 << VMON_TRK1_UV_FS_REACTION_SHIFT) + +/* FS26_FS_I_WD_CFG register */ + +/* Watchdog error counter limit */ +#define WD_ERR_LIMIT_SHIFT (14) +#define WD_ERR_LIMIT_MASK (0x3 << WD_ERR_LIMIT_SHIFT) +#define WD_ERR_LIMIT_8 (0x0 << WD_ERR_LIMIT_SHIFT) +#define WD_ERR_LIMIT_6 (0x1 << WD_ERR_LIMIT_SHIFT) +#define WD_ERR_LIMIT_4 (0x2 << WD_ERR_LIMIT_SHIFT) +#define WD_ERR_LIMIT_2 (0x3 << WD_ERR_LIMIT_SHIFT) + +/* Watchdog refresh counter limit */ +#define WD_RFR_LIMIT_SHIFT (11) +#define WD_RFR_LIMIT_MASK (0x3 << WD_RFR_LIMIT_SHIFT) +#define WD_RFR_LIMIT_6 (0x0 << WD_RFR_LIMIT_SHIFT) +#define WD_RFR_LIMIT_4 (0x1 << WD_RFR_LIMIT_SHIFT) +#define WD_RFR_LIMIT_2 (0x2 << WD_RFR_LIMIT_SHIFT) +#define WD_RFR_LIMIT_1 (0x3 << WD_RFR_LIMIT_SHIFT) + +/* Reaction on RSTB or FAIL SAFE output in case of BAD Watchdog (data or timing) */ +#define WD_FS_REACTION_SHIFT (8) +#define WD_FS_REACTION_MASK (0x3 << WD_FS_REACTION_SHIFT) +#define WD_FS_REACTION_NO_ACTION (0x0 << WD_FS_REACTION_SHIFT) +#define WD_FS_REACTION_FS0B (0x1 << WD_FS_REACTION_SHIFT) +#define WD_FS_REACTION_RSTB_FS0B (0x2 << WD_FS_REACTION_SHIFT) + +/* Reflect the value of the Watchdog Refresh Counter */ +#define WD_RFR_CNT_SHIFT (8) +#define WD_RFR_CNT_MASK (0x7 << WD_RFR_CNT_SHIFT) +#define WD_RFR_CNT(n) ((n) & (0x7 << WD_RFR_CNT_SHIFT)) + +/* Reflect the value of the Watchdog Error Counter */ +#define WD_ERR_CNT_SHIFT (0) +#define WD_ERR_CNT_MASK (0xf << WD_ERR_CNT_SHIFT) +#define WD_ERR_CNT(n) \ + (((n) & (0x7 << WD_RFR_CNT_SHIFT)) > 11) ? (11) : (((n) & (0x7 << WD_RFR_CNT_SHIFT))) + +/* FS26_FS_I_SAFE_INPUTS register */ + +/* FCCU Monitoring Configuration */ +#define FCCU_CFG_SHIFT (13) +#define FCCU_CFG_MASK (0x7 << FCCU_CFG_SHIFT) +#define FCCU_CFG_NO_MONITORING (0x0 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU1_FCCU2_PAIR (0x1 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU1_FCCU2_SINGLE (0x2 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU1_ONLY (0x3 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU2_ONLY (0x4 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU1_FCCU2_PWM (0x5 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU1_PWM_FCCU2_SINGLE (0x6 << FCCU_CFG_SHIFT) +#define FCCU_CFG_FCCU2_PWM_FCCU1_SINGLE (0x7 << FCCU_CFG_SHIFT) + +/* FCCU12 Fault Polarity */ +#define FCCU12_FLT_POL_SHIFT (12) +#define FCCU12_FLT_POL_MASK (0x1 << FCCU12_FLT_POL_SHIFT) +#define FCCU12_FLT_POL_FCCU1_0_FCCU2_1_IS_FAULT (0x0 << FCCU12_FLT_POL_SHIFT) +#define FCCU12_FLT_POL_FCCU1_1_FCCU2_0_IS_FAULT (0x1 << FCCU12_FLT_POL_SHIFT) + +/* FCCU1 Fault Polarity */ +#define FCCU1_FLT_POL_SHIFT (11) +#define FCCU1_FLT_POL_MASK (0x1 << FCCU1_FLT_POL_SHIFT) +#define FCCU1_FLT_POL_LOW (0x0 << FCCU1_FLT_POL_SHIFT) +#define FCCU1_FLT_POL_HIGH (0x1 << FCCU1_FLT_POL_SHIFT) + +/* FCCU2 Fault Polarity */ +#define FCCU2_FLT_POL_SHIFT (10) +#define FCCU2_FLT_POL_MASK (0x1 << FCCU2_FLT_POL_SHIFT) +#define FCCU2_FLT_POL_LOW (0x0 << FCCU2_FLT_POL_SHIFT) +#define FCCU2_FLT_POL_HIGH (0x1 << FCCU2_FLT_POL_SHIFT) + +/* Reaction on RSTB or FAIL SAFE output in case of FAULT DETECTION ON FCCU12 */ +#define FCCU12_FS_REACTION_SHIFT (9) +#define FCCU12_FS_REACTION_MASK (0x1 << FCCU12_FS_REACTION_SHIFT) +#define FCCU12_FS_REACTION (FCCU12_FS_REACTION_MASK) + +/* Reaction on RSTB or FAIL SAFE output in case of FAULT DETECTION ON FCCU1 */ +#define FCCU1_FS_REACTION_SHIFT (8) +#define FCCU1_FS_REACTION_MASK (0x1 << FCCU1_FS_REACTION_SHIFT) +#define FCCU1_FS_REACTION (FCCU1_FS_REACTION_MASK) + +/* Reaction on RSTB or FAIL SAFE output in case of FAULT DETECTION ON FCCU2 */ +#define FCCU2_FS_REACTION_SHIFT (7) +#define FCCU2_FS_REACTION_MASK (0x1 << FCCU2_FS_REACTION_SHIFT) +#define FCCU2_FS_REACTION (FCCU2_FS_REACTION_MASK) + +/* ERRORMON Fault Polarity */ +#define ERRMON_FLT_POLARITY_SHIFT (5) +#define ERRMON_FLT_POLARITY_MASK (0x1 << ERRMON_FLT_POLARITY_SHIFT) +#define ERRMON_FLT_POLARITY_LOW (0x0 << ERRMON_FLT_POLARITY_SHIFT) +#define ERRMON_FLT_POLARITY_HIGH (0x1 << ERRMON_FLT_POLARITY_SHIFT) + +/* Acknowledge timing following a fault detection on ERRMON */ +#define ERRMON_ACK_TIME_SHIFT (3) +#define ERRMON_ACK_TIME_MASK (0x3 << ERRMON_ACK_TIME_SHIFT) +#define ERRMON_ACK_TIME_1MS (0x0 << ERRMON_ACK_TIME_SHIFT) +#define ERRMON_ACK_TIME_8MS (0x1 << ERRMON_ACK_TIME_SHIFT) +#define ERRMON_ACK_TIME_16MS (0x2 << ERRMON_ACK_TIME_SHIFT) +#define ERRMON_ACK_TIME_32MS (0x3 << ERRMON_ACK_TIME_SHIFT) + +/* Reaction on RSTB or Fail Safe output in case of fault detection on ERRMON */ +#define ERRMON_FS_REACTION_SHIFT (2) +#define ERRMON_FS_REACTION_MASK (0x1 << FCCU2_FS_REACTION_SHIFT) +#define ERRMON_FS_REACTION (FCCU2_FS_REACTION_MASK) + +/* FCCU pin filtering time settings */ +#define FCCU12_FILT_SHIFT (0) +#define FCCU12_FILT_MASK (0x3 << FCCU12_FILT_SHIFT) +#define FCCU12_FILT_3US (0x0 << FCCU12_FILT_SHIFT) +#define FCCU12_FILT_6US (0x1 << FCCU12_FILT_SHIFT) +#define FCCU12_FILT_10US (0x2 << FCCU12_FILT_SHIFT) +#define FCCU12_FILT_20US (0x3 << FCCU12_FILT_SHIFT) + +/* FS26_FS_I_FSSM register */ + +/* Configure the maximum level of the fault counter */ +#define FLT_ERR_CNT_LIMIT_SHIFT (14) +#define FLT_ERR_CNT_LIMIT_MASK (0x3 << FLT_ERR_CNT_LIMIT_SHIFT) +#define FLT_ERR_CNT_LIMIT_2 (0x0 << FLT_ERR_CNT_LIMIT_SHIFT) +#define FLT_ERR_CNT_LIMIT_6 (0x1 << FLT_ERR_CNT_LIMIT_SHIFT) +#define FLT_ERR_CNT_LIMIT_8 (0x2 << FLT_ERR_CNT_LIMIT_SHIFT) +#define FLT_ERR_CNT_LIMIT_12 (0x3 << FLT_ERR_CNT_LIMIT_SHIFT) + +/* Configure the RSTB and FS0B behavior when fault error counter ≥ intermediate value */ +#define FLT_ERR_REACTION_SHIFT (8) +#define FLT_ERR_REACTION_MASK (0x3 << FLT_ERR_REACTION_SHIFT) +#define FLT_ERR_REACTION_NO_EFFECT (0x0 << FLT_ERR_REACTION_SHIFT) +#define FLT_ERR_REACTION_FS0B (0x1 << FLT_ERR_REACTION_SHIFT) +#define FLT_ERR_REACTION_RSTB_FS0B (0x2 << FLT_ERR_REACTION_SHIFT) + +/* Reset duration configuration */ +#define RSTB_DUR_SHIFT (9) +#define RSTB_DUR_MASK (0x1 << RSTB_DUR_SHIFT) +#define RSTB_DUR_1MS (RSTB_DUR_MASK) +#define RSTB_DUR_10MS (0) + +/* Assert RSTB in case a short to high is detected on FS0B */ +#define BACKUP_SAFETY_PATH_FS0B_SHIFT (7) +#define BACKUP_SAFETY_PATH_FS0B_MASK (0x1 << BACKUP_SAFETY_PATH_FS0B_SHIFT) +#define BACKUP_SAFETY_PATH_FS0B (BACKUP_SAFETY_PATH_FS0B_MASK) + +/* Assert RSTB in case a short to high is detected on FS1B */ +#define BACKUP_SAFETY_PATH_FS1B_SHIFT (6) +#define BACKUP_SAFETY_PATH_FS1B_MASK (0x1 << BACKUP_SAFETY_PATH_FS1B_SHIFT) +#define BACKUP_SAFETY_PATH_FS1B (BACKUP_SAFETY_PATH_FS1B_MASK) + +/* Disable CLK Monitoring */ +#define CLK_MON_DIS_SHIFT (5) +#define CLK_MON_DIS_MASK (0x1 << CLK_MON_DIS_SHIFT) +#define CLK_MON_DIS (CLK_MON_DIS_MASK) + +/* Disable 8s RSTB timer */ +#define DIS8S_SHIFT (4) +#define DIS8S_MASK (0x1 << DIS8S_SHIFT) +#define DIS8S (DIS8S_MASK) + +/* Reflect the value of the Watchdog Error Counter */ +#define FLT_ERR_CNT_SHIFT (0) +#define FLT_ERR_CNT_MASK (0xf << FLT_ERR_CNT_SHIFT) +#define FLT_ERR_CNT(n) \ + ((n & (0x7 << FLT_ERR_CNT_SHIFT)) > 12) ? (12) : ((n & (0x7 << FLT_ERR_CNT_SHIFT))) + +/* FS26_FS_WDW_DURATION register */ + +/* Watchdog window period */ +#define WDW_PERIOD_SHIFT (12) +#define WDW_PERIOD_MASK (0xf << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_DISABLE (0x0 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_1MS (0x1 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_2MS (0x2 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_3MS (0x3 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_4MS (0x4 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_6MS (0x5 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_8MS (0x6 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_12MS (0x7 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_16MS (0x8 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_24MS (0x9 << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_32MS (0xa << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_64MS (0xb << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_128MS (0xc << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_256MS (0xd << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_512MS (0xe << WDW_PERIOD_SHIFT) +#define WDW_PERIOD_1024MS (0xf << WDW_PERIOD_SHIFT) + +/* Watchdog window duty cycle */ +#define WDW_DC_SHIFT (6) +#define WDW_DC_MASK (0x7 << WDW_DC_SHIFT) +#define WDW_DC_31_68 (0x0 << WDW_PERIOD_SHIFT) +#define WDW_DC_37_62 (0x1 << WDW_PERIOD_SHIFT) +#define WDW_DC_50_50 (0x2 << WDW_PERIOD_SHIFT) +#define WDW_DC_62_37 (0x3 << WDW_PERIOD_SHIFT) +#define WDW_DC_68_31 (0x4 << WDW_PERIOD_SHIFT) + +/* Watchdog window period */ +#define WDW_RECOVERY_SHIFT (0) +#define WDW_RECOVERY_MASK (0xf << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_DISABLE (0x0 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_1MS (0x1 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_2MS (0x2 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_3MS (0x3 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_4MS (0x4 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_6MS (0x5 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_8MS (0x6 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_12MS (0x7 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_16MS (0x8 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_24MS (0x9 << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_32MS (0xa << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_64MS (0xb << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_128MS (0xc << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_256MS (0xd << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_512MS (0xe << WDW_RECOVERY_SHIFT) +#define WDW_RECOVERY_1024MS (0xf << WDW_RECOVERY_SHIFT) + +/* FS26_FS_DIAG_SAFETY1 register */ + +/* Bad WD refresh, Error in the data */ +#define BAD_WD_DATA_SHIFT (10) +#define BAD_WD_DATA_MASK (0x1 << BAD_WD_DATA_SHIFT) +#define BAD_WD_DATA (BAD_WD_DATA_MASK) + +/* Bad WD refresh, Error in the timing */ +#define BAD_WD_TIMING_SHIFT (9) +#define BAD_WD_TIMING_MASK (0x1 << BAD_WD_TIMING_SHIFT) +#define BAD_WD_TIMING (BAD_WD_TIMING_MASK) + +/* ABIST 1 pass */ +#define ABIST1_PASS_SHIFT (8) +#define ABIST1_PASS_MASK (0x1 << ABIST1_PASS_SHIFT) +#define ABIST1_PASS (ABIST1_PASS_MASK) + +/* ABIST 2 pass */ +#define ABIST2_PASS_SHIFT (7) +#define ABIST2_PASS_MASK (0x1 << ABIST2_PASS_SHIFT) +#define ABIST2_PASS (ABIST2_PASS_MASK) + +/* ABIST 2 done */ +#define ABIST2_DONE_SHIFT (6) +#define ABIST2_DONE_MASK (0x1 << ABIST2_DONE_SHIFT) +#define ABIST2_DONE (ABIST2_DONE_MASK) + +/* SPI CLK error */ +#define SPI_FS_CLK_SHIFT (5) +#define SPI_FS_CLK_MASK (0x1 << SPI_FS_CLK_SHIFT) +#define SPI_FS_CLK (SPI_FS_CLK_MASK) + +/* SPI invalid read/write error */ +#define SPI_FS_REQ_SHIFT (4) +#define SPI_FS_REQ_MASK (0x1 << SPI_FS_REQ_SHIFT) +#define SPI_FS_REQ (SPI_FS_REQ_MASK) + +/* SPI CRC error */ +#define SPI_FS_CRC_SHIFT (3) +#define SPI_FS_CRC_MASK (0x1 << SPI_FS_CRC_SHIFT) +#define SPI_FS_CRC (SPI_FS_CRC_MASK) + +/* FS OSC drift */ +#define FS_OSC_DRIFT_SHIFT (2) +#define FS_OSC_DRIFT_MASK (0x1 << FS_OSC_DRIFT_SHIFT) +#define FS_OSC_DRIFT (FS_OSC_DRIFT_MASK) + +/* LBIST STATUS */ +#define LBIST_STATUS_SHIFT (0) +#define LBIST_STATUS_MASK (0x3 << LBIST_STATUS_SHIFT) +#define LBIST_STATUS (LBIST_STATUS_MASK) +#define LBIST_STATUS_FAIL (0x0 << LBIST_STATUS_SHIFT) +#define LBIST_STATUS_BYPASSED (0x1 << LBIST_STATUS_SHIFT) +#define LBIST_STATUS_FAIL2 (0x2 << LBIST_STATUS_SHIFT) +#define LBIST_STATUS_OK (0x3 << LBIST_STATUS_SHIFT) + +/* FS26_FS_STATES register */ + +/* Leave debug mode */ +#define EXIT_DBG_MODE_SHIFT (14) +#define EXIT_DBG_MODE_MASK (0x1 << EXIT_DBG_MODE_SHIFT) +#define EXIT_DBG_MODE (EXIT_DBG_MODE_MASK) + +/* debug mode */ +#define DBG_MODE_SHIFT (13) +#define DBG_MODE_MASK (0x1 << DBG_MODE_SHIFT) +#define DBG_MODE (DBG_MODE_MASK) + +/* OTP crc error */ +#define OTP_CORRUPT_SHIFT (12) +#define OTP_CORRUPT_MASK (0x1 << OTP_CORRUPT_SHIFT) +#define OTP_CORRUPT (OTP_CORRUPT_MASK) + +/* INIT register error */ +#define REG_CORRUPT_SHIFT (11) +#define REG_CORRUPT_MASK (0x1 << REG_CORRUPT_SHIFT) +#define REG_CORRUPT (REG_CORRUPT_MASK) + +/* LBIST STATUS */ +#define FS_STATES_SHIFT (0) +#define FS_STATES_MASK (0x1f << FS_STATES_SHIFT) +#define FS_STATES (FS_STATES_MASK) +#define FS_STATES_DEBUG_ENTRY (0x4 << FS_STATES_SHIFT) +#define FS_STATES_ENABLE_MON (0x6 << FS_STATES_SHIFT) +#define FS_STATES_RSTB_RELEASE (0x8 << FS_STATES_SHIFT) +#define FS_STATES_INIT_FS (0x9 << FS_STATES_SHIFT) +#define FS_STATES_SAFETY_OUT_NOT (0xa << FS_STATES_SHIFT) +#define FS_STATES_NORMAL (0xb << FS_STATES_SHIFT) + +/* FS26_FS_GRL_FLAGS register */ + +/* Report an issue in the communication (SPI) */ +#define FS_COM_G_SHIFT (15) +#define FS_COM_G_MASK (0x1 << FS_COM_G_SHIFT) +#define FS_COM_G (FS_COM_G_MASK) + +/* Report an issue on the Watchdog Refresh */ +#define FS_WD_G_SHIFT (14) +#define FS_WD_G_MASK (0x1 << FS_WD_G_SHIFT) +#define FS_WD_G (FS_WD_G_MASK) + +/* Report an issue in one of the Fail Safe IOs */ +#define FS_IO_G_SHIFT (13) +#define FS_IO_G_MASK (0x1 << FS_IO_G_SHIFT) +#define FS_IO_G (FS_IO_G_MASK) + +/* Report an issue in one of the voltage monitoring (OV or UV) */ +#define FS_REG_OVUV_G_SHIFT (12) +#define FS_REG_OVUV_G_MASK (0x1 << FS_REG_OVUV_G_SHIFT) +#define FS_REG_OVUV_G (FS_REG_OVUV_G_MASK) + +/* Report an issue on BIST (Logical or Analog) */ +#define FS_BIST_G_SHIFT (11) +#define FS_BIST_G_MASK (0x1 << FS_BIST_G_SHIFT) +#define FS_BIST_G (FS_BIST_G_MASK) + +/* FS26_FS_SAFE_IOS_1 register */ + +/* Go Back to INIT Fail Safe Request */ +#define FS_GOTO_INIT_SHIFT (1) +#define FS_GOTO_INIT_MASK (0x1 << FS_GOTO_INIT_SHIFT) +#define FS_GOTO_INIT (FS_GOTO_INIT_MASK) + +/* FS26_FS_INTB_MASK register */ + +/* Interrupt Mask on BAD_WD_REFRESH */ +#define BAD_WD_M (0x1 << 5) + +#endif /* ZEPHYR_DRIVERS_WATCHDOG_WDT_NXP_FS26_H_ */ diff --git a/dts/bindings/watchdog/nxp,fs26-wdog.yaml b/dts/bindings/watchdog/nxp,fs26-wdog.yaml new file mode 100644 index 00000000000..a27f2e9b1a0 --- /dev/null +++ b/dts/bindings/watchdog/nxp,fs26-wdog.yaml @@ -0,0 +1,64 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + FS26 System Basis Chip (SBC) watchdog driver. + + The FS26 features multiple voltage regulators to supply the microcontroller, + peripheral ICs and communication interfaces. The FS26 also offers various + functionalities for system control and monitoring, including a configurable + watchdog counter to ensure the microcontroller is able to communicate with the + FS26, which can react to any failure condition and place the system in a safe + state. This driver covers only the watchdog functionality of FS26. The rest + of the functionalities are not implemented. + + The FS26 uses a 32-bit SPI interface. The MCU is the primary driving MOSI and + FS26 is the secondary driving MISO. Therefore the FS26 devicetree node must be + in a SPI bus. For example, if FS26 is connected to spi3 bus, on Chip Select 0: + + &spi3 { + // here there should be spi3 properties as needed + status = "okay"; + + fs26_wdt: watchdog@0 { + compatible = "nxp,fs26-wdog"; + reg = <0>; + spi-max-frequency = ; + type = "challenger"; + int-gpios = <&gpioa_h 3 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + }; + + When an FS26 internal interrupt occurs, the INTB pin generates a pulse to + inform the microcontroller. The driver masks all interrupt sources except for + bad watchdog refresh (BAD_WD_M). The GPIO pin where the interrupt signal is + received must be configured from devicetree. In the example above, this is + indicated through int-gpios property. It is also required to configure the + external interrupt controller to receive interrupts on this pin. + +compatible: "nxp,fs26-wdog" + +include: spi-device.yaml + +properties: + type: + required: true + type: string + enum: + - simple + - challenger + description: | + Watchdog type enabled on this device. + + The Challenger watchdog monitoring feature is enabled for ASIL D devices. + This mode is based on a question/answer process with the microcontroller. + + The Simple watchdog monitoring feature is enabled for ASIL B devices. This + mode uses a unique seed. + + int-gpios: + type: phandle-array + required: true + description: | + GPIO to use to receive external interrupts from INTB signal.