| /* |
| * Copyright (c) 2022 TOKITA Hiroshi |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT gd_gd32_fwdgt |
| |
| #include <zephyr/drivers/watchdog.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys_clock.h> |
| |
| #include <gd32_fwdgt.h> |
| |
| LOG_MODULE_REGISTER(wdt_fwdgt_gd32, CONFIG_WDT_LOG_LEVEL); |
| |
| #define FWDGT_RELOAD_MAX (0xFFFU) |
| #define FWDGT_PRESCALER_MAX (256U) |
| |
| #if defined(CONFIG_GD32_HAS_IRC_32K) |
| #define RCU_IRC_LOW_SPEED RCU_IRC32K |
| #elif defined(CONFIG_GD32_HAS_IRC_40K) |
| #define RCU_IRC_LOW_SPEED RCU_IRC40K |
| #else |
| #error IRC frequency was not configured |
| #endif |
| |
| #define IS_VALID_FWDGT_PRESCALER(psc) \ |
| (((psc) == FWDGT_PSC_DIV4) || ((psc) == FWDGT_PSC_DIV8) || \ |
| ((psc) == FWDGT_PSC_DIV16) || ((psc) == FWDGT_PSC_DIV32) || \ |
| ((psc) == FWDGT_PSC_DIV64) || ((psc) == FWDGT_PSC_DIV128) || \ |
| ((psc) == FWDGT_PSC_DIV256)) |
| |
| #define FWDGT_INITIAL_TIMEOUT DT_INST_PROP(0, initial_timeout_ms) |
| |
| #if (FWDGT_INITIAL_TIMEOUT <= 0) |
| #error Must be initial-timeout > 0 |
| #elif (FWDGT_INITIAL_TIMEOUT > \ |
| (FWDGT_PRESCALER_MAX * FWDGT_RELOAD_MAX * MSEC_PER_SEC / \ |
| CONFIG_GD32_LOW_SPEED_IRC_FREQUENCY)) |
| #error Must be initial-timeout <= (256 * 4095 * 1000 / GD32_LOW_SPEED_IRC_FREQUENCY) |
| #endif |
| |
| /** |
| * @brief Calculates FWDGT config value from timeout. |
| * |
| * @param timeout Timeout value in milliseconds. |
| * @param prescaler Pointer to the storage of prescaler value. |
| * @param reload Pointer to the storage of reload value. |
| * |
| * @return 0 on success, -EINVAL if the timeout is out of range |
| */ |
| static int gd32_fwdgt_calc_timeout(uint32_t timeout, uint32_t *prescaler, |
| uint32_t *reload) |
| { |
| uint16_t divider = 4U; |
| uint8_t shift = 0U; |
| uint32_t ticks = (uint64_t)CONFIG_GD32_LOW_SPEED_IRC_FREQUENCY * |
| timeout / MSEC_PER_SEC; |
| |
| while ((ticks / divider) > FWDGT_RELOAD_MAX) { |
| shift++; |
| divider = 4U << shift; |
| } |
| |
| if (!IS_VALID_FWDGT_PRESCALER(PSC_PSC(shift)) || timeout == 0U) { |
| return -EINVAL; |
| } |
| |
| /* convert the 'shift' to prescaler value */ |
| *prescaler = PSC_PSC(shift); |
| *reload = (ticks / divider) - 1U; |
| |
| return 0; |
| } |
| |
| static int gd32_fwdgt_setup(const struct device *dev, uint8_t options) |
| { |
| ARG_UNUSED(dev); |
| |
| if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) != 0U) { |
| #if CONFIG_GD32_DBG_SUPPORT |
| dbg_periph_enable(DBG_FWDGT_HOLD); |
| #else |
| LOG_ERR("Debug support not enabled"); |
| return -ENOTSUP; |
| #endif |
| } |
| |
| if ((options & WDT_OPT_PAUSE_IN_SLEEP) != 0U) { |
| LOG_ERR("WDT_OPT_PAUSE_IN_SLEEP not supported"); |
| return -ENOTSUP; |
| } |
| |
| fwdgt_enable(); |
| |
| return 0; |
| } |
| |
| static int gd32_fwdgt_disable(const struct device *dev) |
| { |
| /* watchdog cannot be stopped once started */ |
| ARG_UNUSED(dev); |
| |
| return -EPERM; |
| } |
| |
| static int gd32_fwdgt_install_timeout(const struct device *dev, |
| const struct wdt_timeout_cfg *config) |
| { |
| uint32_t prescaler = 0U; |
| uint32_t reload = 0U; |
| ErrStatus errstat = ERROR; |
| |
| /* Callback is not supported by FWDGT */ |
| if (config->callback != NULL) { |
| LOG_ERR("callback not supported by FWDGT"); |
| return -ENOTSUP; |
| } |
| |
| /* Calculate prescaler and reload value from timeout value */ |
| if (gd32_fwdgt_calc_timeout(config->window.max, &prescaler, |
| &reload) != 0) { |
| LOG_ERR("window max is out of range"); |
| return -EINVAL; |
| } |
| |
| /* Configure and run FWDGT */ |
| fwdgt_write_enable(); |
| errstat = fwdgt_config(reload, prescaler); |
| if (errstat != SUCCESS) { |
| LOG_ERR("fwdgt_config() failed: %d", errstat); |
| return -EINVAL; |
| } |
| fwdgt_write_disable(); |
| |
| return 0; |
| } |
| |
| static int gd32_fwdgt_feed(const struct device *dev, int channel_id) |
| { |
| ARG_UNUSED(channel_id); |
| |
| fwdgt_counter_reload(); |
| |
| return 0; |
| } |
| |
| static const struct wdt_driver_api fwdgt_gd32_api = { |
| .setup = gd32_fwdgt_setup, |
| .disable = gd32_fwdgt_disable, |
| .install_timeout = gd32_fwdgt_install_timeout, |
| .feed = gd32_fwdgt_feed, |
| }; |
| |
| static int gd32_fwdgt_init(const struct device *dev) |
| { |
| int ret = 0; |
| |
| /* Turn on and wait stabilize system clock oscillator. */ |
| rcu_osci_on(RCU_IRC_LOW_SPEED); |
| while (!rcu_osci_stab_wait(RCU_IRC_LOW_SPEED)) { |
| } |
| |
| #if !defined(CONFIG_WDT_DISABLE_AT_BOOT) |
| const struct wdt_timeout_cfg config = { |
| .window.max = FWDGT_INITIAL_TIMEOUT |
| }; |
| |
| ret = gd32_fwdgt_install_timeout(dev, &config); |
| #endif |
| |
| return ret; |
| } |
| |
| DEVICE_DT_INST_DEFINE(0, gd32_fwdgt_init, NULL, NULL, NULL, POST_KERNEL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &fwdgt_gd32_api); |