/* * Copyright (c) 2021-2025 HPMicro * * SPDX-License-Identifier: BSD-3-Clause * * Change Logs: * Date Author Notes * 2022-05-08 HPMicro the first version * 2025-09-01 HPMicro Revised the code, reduced repetitive codes, removed interrupt related codes */ #include "rtconfig.h" #ifdef BSP_USING_WDG #include "board.h" #include "drv_wdt.h" #include "hpm_wdg_drv.h" #include #include #include #include "hpm_sysctl_drv.h" /* Macros to reduce repetitive code */ #define WDG_DECLARE(n) rt_watchdog_t wdog##n #define WDG_DEVICE_INIT(n) \ { \ .wdog_base = HPM_WDG## n, \ .device_name = "wdt" #n, \ .clock_name = clock_watchdog## n, \ .wdog = &wdog## n, \ .wdog_ctrl = \ { \ .reset_interval = reset_interval_clock_period_mult_16k, \ .interrupt_interval = interrupt_interval_clock_period_multi_8k, \ .reset_enable = true, \ .interrupt_enable = false, \ .clksrc = wdg_clksrc_extclk, \ .wdg_enable = false, \ }, \ }, #define WDG_CTX(wdt) \ hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)(wdt)->parent.user_data; \ WDG_Type *base = hpm_wdog->wdog_base typedef struct hpm_wdog { WDG_Type *wdog_base; char *device_name; clock_name_t clock_name; rt_watchdog_t *wdog; wdg_control_t wdog_ctrl; } hpm_wdog_t; static rt_err_t hpm_wdog_init(rt_watchdog_t *wdt); static rt_err_t hpm_wdog_open(rt_watchdog_t *wdt, rt_uint16_t oflag); static rt_err_t hpm_wdog_close(rt_watchdog_t *wdt); static rt_err_t hpm_wdog_refresh(rt_watchdog_t *wdt); static rt_err_t hpm_wdog_control(rt_watchdog_t *wdt, int cmd, void *args); #if defined(BSP_USING_WDG0) WDG_DECLARE(0); #endif #if defined(BSP_USING_WDG1) WDG_DECLARE(1); #endif #if defined(BSP_USING_WDG2) WDG_DECLARE(2); #endif #if defined(BSP_USING_WDG3) WDG_DECLARE(3); #endif static hpm_wdog_t wdogs[] = { #ifdef BSP_USING_WDG0 WDG_DEVICE_INIT(0) #endif #ifdef BSP_USING_WDG1 WDG_DEVICE_INIT(1) #endif #ifdef BSP_USING_WDG2 WDG_DEVICE_INIT(2) #endif #ifdef BSP_USING_WDG3 WDG_DEVICE_INIT(3) #endif }; static struct rt_watchdog_ops hpm_wdog_ops = { .init = hpm_wdog_init, .control = hpm_wdog_control, }; static rt_err_t hpm_wdog_init(rt_watchdog_t *wdt) { hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; clock_add_to_group(hpm_wdog->clock_name, BOARD_RUNNING_CORE & 0x1); wdg_init(base, &hpm_wdog->wdog_ctrl); return RT_EOK; } static rt_err_t hpm_wdog_open(rt_watchdog_t *wdt, rt_uint16_t oflag) { hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; rt_enter_critical(); wdg_enable(base); rt_exit_critical(); return RT_EOK; } static rt_err_t hpm_wdog_close(rt_watchdog_t *wdt) { hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; rt_enter_critical(); wdg_disable(base); rt_exit_critical(); return RT_EOK; } static rt_err_t hpm_wdog_refresh(rt_watchdog_t *wdt) { hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; rt_enter_critical(); wdg_restart(base); rt_exit_critical(); return RT_EOK; } static rt_err_t hpm_wdog_control(rt_watchdog_t *wdt, int cmd, void *args) { rt_err_t ret = RT_EOK; hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; wdg_control_t *wdog_ctrl = &hpm_wdog->wdog_ctrl; uint32_t temp; switch (cmd) { case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: if (args == RT_NULL) { return -RT_EINVAL; } temp = wdg_get_total_reset_interval_in_us(base, WDG_EXT_CLK_FREQ); temp /= 1000000UL; /* Convert to seconds */ *(uint32_t *)args = temp; break; case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: if ((args == RT_NULL) || (*(uint32_t *)args == 0U)) { return -RT_EINVAL; } temp = *(uint32_t *)args; temp *= 1000000U; /* Convert to microseconds */ wdog_ctrl->interrupt_interval = wdg_convert_interrupt_interval_from_us(WDG_EXT_CLK_FREQ, temp); wdog_ctrl->reset_interval = reset_interval_clock_period_mult_128; /* Intentionally hard-coded */ wdog_ctrl->reset_enable = true; wdog_ctrl->interrupt_enable = true; wdog_ctrl->clksrc = wdg_clksrc_extclk; wdog_ctrl->wdg_enable = false; hpm_wdog_init(wdt); break; case RT_DEVICE_CTRL_WDT_KEEPALIVE: hpm_wdog_refresh(wdt); break; case RT_DEVICE_CTRL_WDT_START: if (args == RT_NULL) { return -RT_EINVAL; } hpm_wdog_open(wdt, *(uint16_t *)args); break; case RT_DEVICE_CTRL_WDT_STOP: hpm_wdog_close(wdt); break; default: ret = -RT_EINVAL; break; } return ret; } void hpm_wdog_isr(rt_watchdog_t *wdt) { hpm_wdog_t *hpm_wdog = (hpm_wdog_t *)wdt->parent.user_data; WDG_Type *base = hpm_wdog->wdog_base; uint32_t status = wdg_get_status(base); if (IS_HPM_BITMASK_SET(status, WDG_ST_INTEXPIRED_MASK)) { wdg_clear_status(base, WDG_ST_INTEXPIRED_MASK); } } int rt_hw_wdt_init(void) { rt_err_t err = RT_EOK; for (uint32_t i = 0; i < sizeof(wdogs) / sizeof(wdogs[0]); i++) { wdogs[i].wdog->ops = &hpm_wdog_ops; clock_add_to_group(wdogs[i].clock_name, BOARD_RUNNING_CORE & 0x1); err = rt_hw_watchdog_register(wdogs[i].wdog, wdogs[i].device_name, RT_DEVICE_FLAG_RDWR, (void *)&wdogs[i]); if (err != RT_EOK) { LOG_E("rt device %s failed, status=%d\n", wdogs[i].device_name, err); } } return err; } INIT_BOARD_EXPORT(rt_hw_wdt_init); #endif /* RT_USING_WDT */