diff --git a/bsp/k230/.config b/bsp/k230/.config index 67267cd508..b019e93de7 100644 --- a/bsp/k230/.config +++ b/bsp/k230/.config @@ -1497,6 +1497,8 @@ CONFIG_BSP_USING_SDIO0=y # CONFIG_BSP_SDIO0_1V8 is not set # CONFIG_BSP_USING_SDIO1 is not set CONFIG_BSP_SD_MNT_DEVNAME="sd0p1" +# CONFIG_BSP_USING_WDT is not set +# CONFIG_BSP_UTEST_DRIVERS is not set # end of Drivers Configuration CONFIG_BOARD_fpgac908=y diff --git a/bsp/k230/board/Kconfig b/bsp/k230/board/Kconfig index 93d3df2c38..c91fed4f61 100644 --- a/bsp/k230/board/Kconfig +++ b/bsp/k230/board/Kconfig @@ -39,4 +39,25 @@ menu "Drivers Configuration" default "sd0p1" endif + menuconfig BSP_USING_WDT + bool "Enable Watchdog Timer" + select RT_USING_WDT + default n + + if BSP_USING_WDT + config BSP_USING_WDT0 + bool "Enable WDT0" + default n + + config BSP_USING_WDT1 + bool "Enable WDT1" + default n + + endif + + config BSP_UTEST_DRIVERS + bool "Enable drivers utest" + select RT_USING_UTEST + default n + endmenu diff --git a/bsp/k230/drivers/interdrv/wdt/SConscript b/bsp/k230/drivers/interdrv/wdt/SConscript new file mode 100644 index 0000000000..d7c561ee12 --- /dev/null +++ b/bsp/k230/drivers/interdrv/wdt/SConscript @@ -0,0 +1,19 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('WDT', src, depend = ['BSP_USING_WDT'], CPPPATH = CPPPATH) + +objs = [group] + +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/k230/drivers/interdrv/wdt/drv_wdt.c b/bsp/k230/drivers/interdrv/wdt/drv_wdt.c new file mode 100644 index 0000000000..296f1cf561 --- /dev/null +++ b/bsp/k230/drivers/interdrv/wdt/drv_wdt.c @@ -0,0 +1,274 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "board.h" +#include "ioremap.h" +#include "sysctl_clk.h" +#include "drv_wdt.h" +#include +#include + +struct k230_wdt_dev +{ + struct rt_watchdog_device device; + const char *name; + rt_ubase_t base; + size_t size; + sysctl_clk_node_e clk; /* clock source */ +}; + +/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ + +static const rt_uint32_t *tops; + +static const rt_uint32_t k230_wdt_fix_tops[KD_WDT_NUM_TOPS] = { + KD_WDT_FIX_TOP(0), KD_WDT_FIX_TOP(1), KD_WDT_FIX_TOP(2), + KD_WDT_FIX_TOP(3), KD_WDT_FIX_TOP(4), KD_WDT_FIX_TOP(5), + KD_WDT_FIX_TOP(6), KD_WDT_FIX_TOP(7), KD_WDT_FIX_TOP(8), + KD_WDT_FIX_TOP(9), KD_WDT_FIX_TOP(10), KD_WDT_FIX_TOP(11), + KD_WDT_FIX_TOP(12), KD_WDT_FIX_TOP(13), KD_WDT_FIX_TOP(14), + KD_WDT_FIX_TOP(15) +}; + +static struct k230_wdt_timeout timeouts[KD_WDT_NUM_TOPS]; +static char rmod; /* wdt reset mode, */ + +static void k230_wdt_timeouts_init(struct k230_wdt_dev *dev) +{ + rt_uint32_t wdt_clk; + rt_uint32_t time_value; + rt_uint32_t i, t; + rt_uint64_t msec; + struct k230_wdt_timeout tout, *dst; + /* caculate timeout value */ + wdt_clk = sysctl_clk_get_leaf_freq(dev->clk); + + for (i = 0; i < KD_WDT_NUM_TOPS; ++i) + { + tout.top_val = i; + tout.sec = tops[i] / wdt_clk; + msec = (rt_uint64_t)tops[i] * (rt_uint64_t)1000L; + msec = msec / wdt_clk; + tout.msec = msec - (rt_uint64_t)tout.sec * (rt_uint64_t)1000L; + + for (t = 0; t < i; ++t) + { + dst = &timeouts[t]; + if (tout.sec > dst->sec || (tout.sec == dst->sec && + tout.msec >= dst->msec)) + continue; + else + swap(*dst, tout); + } + + timeouts[i] = tout; + } + rt_kprintf("watchdog timeout table init OK!\n"); +} + +static rt_err_t k230_wdt_feed(struct k230_wdt_dev *dev) +{ + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + reg->crr = 0x76; + + return RT_EOK; +} + +static rt_err_t k230_wdt_enable(struct k230_wdt_dev *dev) +{ + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + reg->crr = 0x76; + reg->cr |= 0x1; + + return RT_EOK; +} + +static rt_err_t k230_wdt_disable(struct k230_wdt_dev *dev) +{ + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + reg->crr = 0x76; + reg->cr &= ~0x1; + + return RT_EOK; +} + +static rt_err_t k230_wdt_set_timeout(struct k230_wdt_dev *dev, rt_uint64_t timeout) +{ + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + rt_uint32_t top_val; + rt_uint32_t i; + rt_uint32_t time = (timeout + rmod -1) / rmod; + + for (i = 0; i < KD_WDT_NUM_TOPS; ++i) + { + if (timeouts[i].sec >= time) + break; + } + + if (i == KD_WDT_NUM_TOPS) + --i; + + top_val = timeouts[i].top_val; + + reg->torr = (top_val << 4) | (top_val << 0); + + return RT_EOK; +} + +static rt_err_t k230_wdt_get_timeout(struct k230_wdt_dev *dev, void *timeout) +{ + rt_uint64_t top_val; + rt_uint32_t i; + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + top_val = ((reg->torr) & 0xf); + + for (i = 0; i < KD_WDT_NUM_TOPS; ++i) + { + if (timeouts[i].top_val == top_val) + break; + } + + *((rt_uint64_t *)timeout) = timeouts[i].sec * rmod; + + return RT_EOK; +} + +static rt_err_t k230_wdt_init(rt_watchdog_t *wdt) +{ + RT_ASSERT(wdt != NULL); + + struct k230_wdt_dev *dev = rt_container_of(wdt, struct k230_wdt_dev, device); + k230_wdt_t *reg = (k230_wdt_t *)dev->base; + + reg->cr &= ~(0x01 << 1); + reg->cr |= (0x0 << 1); /* default set wdt reset mode */ + rmod = KD_WDT_RMOD_RESET; + + if (reg->comp_param_1 & (1 << 6)) + { + tops = k230_wdt_fix_tops; + } + + k230_wdt_timeouts_init(dev); + + if (!timeouts[KD_WDT_NUM_TOPS - 1].sec) + { + rt_kprintf("No any valid Timeout period detected\n"); + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t k230_wdt_control(rt_watchdog_t *wdt, int cmd, void *args) +{ + RT_ASSERT(wdt != NULL); + + struct k230_wdt_dev *dev = rt_container_of(wdt, struct k230_wdt_dev, device); + + switch(cmd) + { + case KD_DEVICE_CTRL_WDT_GET_TIMEOUT: + k230_wdt_get_timeout(dev, args); + break; + case KD_DEVICE_CTRL_WDT_SET_TIMEOUT: + k230_wdt_set_timeout(dev, *((rt_uint32_t*)args)); + break; + case KD_DEVICE_CTRL_WDT_KEEPALIVE: + k230_wdt_feed(dev); + break; + case KD_DEVICE_CTRL_WDT_START: + k230_wdt_enable(dev); + break; + case RT_DEVICE_CTRL_WDT_STOP: + case KD_DEVICE_CTRL_WDT_STOP: + k230_wdt_disable(dev); + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static struct rt_watchdog_ops k230_wdt_ops = +{ + .init = k230_wdt_init, + .control = k230_wdt_control, +}; + +static struct k230_wdt_dev wdt_devices[] = +{ +#ifdef BSP_USING_WDT0 + { + .name = "wdt0", + .base = WDT0_BASE_ADDR, + .size = WDT0_IO_SIZE, + .clk = SYSCTL_CLK_WDT0, + }, +#endif /* BSP_USING_WDT0 */ + +#ifdef BSP_USING_WDT1 + { + .name = "wdt1", + .base = WDT1_BASE_ADDR, + .size = WDT1_IO_SIZE, + .clk = SYSCTL_CLK_WDT1, + }, +#endif /* BSP_USING_WDT1 */ + +#if !defined(BSP_USING_WDT0) && !defined(BSP_USING_WDT1) +#error "No watchdog device defined!" +#endif +}; + +int rt_hw_wdt_init(void) +{ + rt_uint8_t i; + for (i = 0; i < sizeof(wdt_devices) / sizeof(struct k230_wdt_dev); i++) + { + wdt_devices[i].device.ops = &k230_wdt_ops; + wdt_devices[i].base = (rt_ubase_t)rt_ioremap((void *)wdt_devices[i].base, wdt_devices[i].size); + + if (rt_hw_watchdog_register(&wdt_devices[i].device, wdt_devices[i].name, RT_DEVICE_FLAG_RDWR, RT_NULL) != RT_EOK) + { + LOG_E("%s register failed!", wdt_devices[i].name); + return -RT_ERROR; + } + LOG_D("%s register OK!", wdt_devices[i].name); + } + + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_wdt_init); \ No newline at end of file diff --git a/bsp/k230/drivers/interdrv/wdt/drv_wdt.h b/bsp/k230/drivers/interdrv/wdt/drv_wdt.h new file mode 100644 index 0000000000..6afb2b0e48 --- /dev/null +++ b/bsp/k230/drivers/interdrv/wdt/drv_wdt.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DRV_WDT_H_ +#define DRV_WDT_H_ + +#include +#include + +#define DEFUALT_TIMEOUT 30*1000 /* ms */ +#define KD_WDT_NUM_TOPS 16 +#define KD_WDT_FIX_TOP(_idx) (1U << (16 + _idx)) +#define KD_WDT_RMOD_RESET 1 +#define KD_WDT_RMOD_IRQ 2 + +#define KD_DEVICE_CTRL_WDT_GET_TIMEOUT _IOW('W', 1, int) /* get timeout(in seconds) */ +#define KD_DEVICE_CTRL_WDT_SET_TIMEOUT _IOW('W', 2, int) /* set timeout(in seconds) */ +#define KD_DEVICE_CTRL_WDT_GET_TIMELEFT _IOW('W', 3, int) /* get the left time before reboot(in seconds) */ +#define KD_DEVICE_CTRL_WDT_KEEPALIVE _IOW('W', 4, int) /* refresh watchdog */ +#define KD_DEVICE_CTRL_WDT_START _IOW('W', 5, int) /* start watchdog */ +#define KD_DEVICE_CTRL_WDT_STOP _IOW('W', 6, int) /* stop watchdog */ + +struct k230_wdt_timeout { + rt_uint32_t top_val; + rt_uint32_t sec; + rt_uint32_t msec; +}; + +#define swap(a, b) \ + do { struct k230_wdt_timeout __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +/* wdt register map */ +typedef struct _wdt +{ + /* WDT Control Register (0x00) */ + volatile uint32_t cr; + /* WDT Timeout Range Register (0x04) */ + volatile uint32_t torr; + /* WDT Current Counter Value Register (0x08) */ + volatile uint32_t ccvr; + /* WDT Counter Restart Register (0x0c) */ + volatile uint32_t crr; + /* WDT Interrupt Status Register (0x10) */ + volatile uint32_t stat; + /* WDT Interrupt Clear Register (0x14) */ + volatile uint32_t eoi; + /* reserverd (0x18) */ + volatile uint32_t resv1; + /* WDT Protection level Register (0x1c) */ + volatile uint32_t prot_level; + /* reserved (0x20-0xe0) */ + volatile uint32_t resv4[49]; + /* WDT Component Parameters Register 5 (0xe4) */ + volatile uint32_t comp_param_5; + /* WDT Component Parameters Register 4 (0xe8) */ + volatile uint32_t comp_param_4; + /* WDT Component Parameters Register 3 (0xec) */ + volatile uint32_t comp_param_3; + /* WDT Component Parameters Register 2 (0xf0) */ + volatile uint32_t comp_param_2; + /* WDT Component Parameters Register 1 (0xf4) */ + volatile uint32_t comp_param_1; + /* WDT Component Version Register (0xf8) */ + volatile uint32_t comp_version; + /* WDT Component Type Register (0xfc) */ + volatile uint32_t comp_type; +} __attribute__((packed, aligned(4))) k230_wdt_t; + + +#endif /* DRV_WDT_H_ */ \ No newline at end of file diff --git a/bsp/k230/drivers/utest/SConscript b/bsp/k230/drivers/utest/SConscript new file mode 100644 index 0000000000..d8032f3923 --- /dev/null +++ b/bsp/k230/drivers/utest/SConscript @@ -0,0 +1,12 @@ +from building import * + +src = [] + +if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('BSP_UTEST_DRIVERS'): + + if GetDepend('BSP_USING_WDT'): + src += ['test_wdt.c'] + +group = DefineGroup('utestcases', src, depend = ['']) + +Return('group') diff --git a/bsp/k230/drivers/utest/test_wdt.c b/bsp/k230/drivers/utest/test_wdt.c new file mode 100644 index 0000000000..6736ca7f34 --- /dev/null +++ b/bsp/k230/drivers/utest/test_wdt.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Refer to https://www.rt-thread.org/document/api/iwdg_sample_8c-example.html + * This is a watchdog device test routine, the program searches for the watchdog + * device by device name, then initializes the device and sets the watchdog + * device overflow time. + * + * "test_wdt_feed" tests that the watchdog timer will not time out when the + * watchdog is fed and will not cause the system to restart. + * + * "test_wdt_nofeed" tests that the watchdog timer will time out when the + * watchdog is not fed and cause the system to restart. + */ + +#include +#include + +#include "../interdrv/wdt/drv_wdt.h" +#include "utest.h" + +/* Default watchdog device name */ +/* If WDT0 and WDT1 both enabled, we use WDT0 */ +#ifdef BSP_USING_WDT0 +#define IWDG_DEVICE_NAME "wdt0" +#elif defined(BSP_USING_WDT1) +#define IWDG_DEVICE_NAME "wdt1" +#else +#error "No watchdog device defined!" +#endif + +/* Watchdog device handle */ +static rt_device_t wdg_dev; + +/* + * Dog feeding flag, 1 means feeding the dog, 0 means not feeding the dog. + * This flag is used in the idle thread callback function. + * If the dog is not fed, the system will restart due to watchdog timeout. + */ +static int flag_feed; + +/* Overflow time, in seconds. */ +static rt_uint32_t timeout = 3; + +static void idle_hook(void) +{ + /* Feed the dog in the callback function of the idle thread */ + if (flag_feed) + rt_device_control(wdg_dev, KD_DEVICE_CTRL_WDT_KEEPALIVE, NULL); + + /* Do not print, otherwise the screen will display too much. */ +} + +static void waiting_for_timeout(int seconds) +{ + LOG_I("Waiting for watchdog timer time-out...\n"); + for (int i = 0; i < seconds; i++) + { + rt_thread_mdelay(1000); + LOG_I("."); + } +} + +static void test_wdt_feed(void) +{ + rt_err_t ret = RT_EOK; + + /* Find the watchdog device according to the name and obtain the handle */ + wdg_dev = rt_device_find(IWDG_DEVICE_NAME); + if (!wdg_dev) + { + LOG_E("find %s failed!\n", IWDG_DEVICE_NAME); + return; + } + + /* Initialize the device */ + ret = rt_device_init(wdg_dev); + if (ret != RT_EOK) + { + LOG_E("initialize %s failed!\n", IWDG_DEVICE_NAME); + return; + } + + /* Set the watchdog timeout time */ + ret = rt_device_control(wdg_dev, KD_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout); + if (ret != RT_EOK) + { + LOG_E("set %s timeout failed!\n", IWDG_DEVICE_NAME); + return; + } + + flag_feed = 1; /* Set feed the dog sign */ + + /* start watchdog timer */ + rt_device_control(wdg_dev, KD_DEVICE_CTRL_WDT_START, RT_NULL); + LOG_I("Watchdog Timer [%s] is launched!\n", IWDG_DEVICE_NAME); + + /* Delay enough time to see if the system will be restarted by the watchdog */ + waiting_for_timeout(10); + + LOG_I("Thanks for feeding me, I'm still alive!\n"); + /* If you feed the dog successfully, you will have a chance to come here and close the gate dog. */ + rt_device_close(wdg_dev); + LOG_I("Watchdog is closed!\n"); + + LOG_I("Test Done with Feed!\n"); + + return; +} + +static void test_wdt_nofeed(void) +{ + rt_err_t ret = RT_EOK; + + /* Find the watchdog device according to the name and obtain the handle */ + wdg_dev = rt_device_find(IWDG_DEVICE_NAME); + if (!wdg_dev) + { + LOG_E("find %s failed!\n", IWDG_DEVICE_NAME); + return; + } + + /* Initialize the device */ + ret = rt_device_init(wdg_dev); + if (ret != RT_EOK) + { + LOG_E("initialize %s failed!\n", IWDG_DEVICE_NAME); + return; + } + + /* Set the watchdog timeout time */ + ret = rt_device_control(wdg_dev, KD_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout); + if (ret != RT_EOK) + { + LOG_E("set %s timeout failed!\n", IWDG_DEVICE_NAME); + return; + } + + flag_feed = 0; /* Do not feed the dog */ + + rt_device_control(wdg_dev, KD_DEVICE_CTRL_WDT_START, RT_NULL); + LOG_I("Watchdog Timer [%s] is launched!\n", IWDG_DEVICE_NAME); + + /* Delay long enough and the system should reboot due to watchdog timeout. */ + LOG_I("Oops, I am so hungary and will be killed in seconds!\n"); + waiting_for_timeout(10); + + LOG_I("SHOULD NOT SEE THIS PRINT!\n"); + + return; +} + +static void test_wdt (void) +{ + UTEST_UNIT_RUN(test_wdt_feed); + UTEST_UNIT_RUN(test_wdt_nofeed); +} + +static rt_err_t utest_init(void) +{ + flag_feed = 0; + rt_thread_idle_sethook(idle_hook); + return RT_EOK; +} + +UTEST_TC_EXPORT(test_wdt, "wdt", utest_init, NULL, 10); \ No newline at end of file