rt-thread-official/bsp/hc32/libraries/hc32_drivers/drv_wdt.c

445 lines
13 KiB
C

/*
* Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-08 CDT first version
* 2023-12-01 CDT added swdt support
*/
#include "board.h"
#ifdef BSP_USING_WDT_TMR
#include <math.h>
#include <string.h>
// #define DRV_DEBUG
#define LOG_TAG "drv_wdt"
#include <drv_log.h>
enum
{
WDT_INIT_ING,
WDT_INIT_OVER,
WDT_IS_ENABLE
};
static struct rt_watchdog_ops _ops;
#ifdef BSP_USING_WDT
struct hc32_wdt_obj
{
rt_watchdog_t watchdog;
stc_wdt_init_t stcwdg;
rt_uint32_t pclk3;
rt_uint8_t sta;
rt_uint8_t index;
};
static struct hc32_wdt_obj hc32_wdt;
struct time_match
{
uint32_t u32ClockDiv;
uint32_t u32CountPeriod;
float timeout_s;
};
static uint32_t const Div[] = {4U, 64U, 128U, 256U, 512U, 1024U, 2048U, 8192U};
static uint32_t const Peri[] = {256U, 4096U, 16384U, 65536U};
static struct time_match wdt_match[(sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))];
static void wdt_match_init(uint32_t clock)
{
int i, j;
for (i = 0; i < (sizeof(Div) / sizeof(Div[0])); i++)
{
for (j = 0; j < (sizeof(Peri) / sizeof(Peri[0])); j++)
{
wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32ClockDiv = Div[i];
wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32CountPeriod = Peri[j];
wdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].timeout_s = (Div[i] * Peri[j]) / (float)clock;
}
}
}
static void wdt_match_sort(void)
{
int i, j;
struct time_match Temp;
/* bubble sort */
for (i = 0; i < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - 1); i++)
{
for (j = 0; j < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - i - 1); j++)
{
if (wdt_match[j].timeout_s > wdt_match[j + 1].timeout_s)
{
memcpy(&Temp, &wdt_match[j], sizeof(struct time_match));
memcpy(&wdt_match[j], &wdt_match[j + 1], sizeof(struct time_match));
memcpy(&wdt_match[j + 1], &Temp, sizeof(struct time_match));
}
}
}
}
static int wdt_match_find_index(uint32_t time_out)
{
int i;
/* Min and Max case */
if (time_out <= wdt_match[0].timeout_s)
{
return 0;
}
else if (time_out >= wdt_match[((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1].timeout_s)
{
return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
}
/* Other case */
for (i = 1; i < (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1); i++)
{
if (time_out >= wdt_match[i].timeout_s && time_out < wdt_match[i + 1].timeout_s)
{
/* Min difference */
if (time_out - wdt_match[i].timeout_s < wdt_match[i + 1].timeout_s - time_out)
{
return i;
}
else
{
return (i + 1);
}
}
}
/* Not match case */
return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
}
static rt_uint32_t wdt_match_find_period(rt_uint32_t Period)
{
rt_uint32_t CountPeriod = 0U;
switch (Period)
{
case 256U:
CountPeriod = WDT_CNT_PERIOD256;
break;
case 4096U:
CountPeriod = WDT_CNT_PERIOD4096;
break;
case 16384U:
CountPeriod = WDT_CNT_PERIOD16384;
break;
case 65536U:
CountPeriod = WDT_CNT_PERIOD65536;
break;
default:
break;
}
return CountPeriod;
}
static rt_uint32_t wdt_get_timeout_s(void)
{
/* timeout(s) = PERI * DIV / PCLK3 */
return ((rt_uint32_t)(wdt_match[hc32_wdt.index].u32CountPeriod * wdt_match[hc32_wdt.index].u32ClockDiv / (float)hc32_wdt.pclk3));
}
static rt_uint32_t wdt_get_timeleft_s(void)
{
/* wdt is down counter */
return ((rt_uint32_t)(WDT_GetCountValue() * wdt_match[hc32_wdt.index].u32ClockDiv / (float)hc32_wdt.pclk3));
}
static rt_err_t _wdt_init(rt_watchdog_t *wdt)
{
hc32_wdt.pclk3 = CLK_GetBusClockFreq(CLK_BUS_PCLK3);
if (!hc32_wdt.pclk3)
{
LOG_E("pclk3 getbusclockfreq failed.");
return -RT_ERROR;
}
wdt_match_init(hc32_wdt.pclk3);
wdt_match_sort();
hc32_wdt.stcwdg.u32RefreshRange = WDT_RANGE_0TO100PCT;
#ifdef BSP_WDT_CONTINUE_COUNT
hc32_wdt.stcwdg.u32LPMCount = WDT_LPM_CNT_CONTINUE;
#else
hc32_wdt.stcwdg.u32LPMCount = WDT_LPM_CNT_STOP;
#endif
hc32_wdt.stcwdg.u32ExceptionType = WDT_EXP_TYPE_RST;
hc32_wdt.sta = WDT_INIT_ING;
/* WDT_CR register only support write once,so can't call WDT_Init of ther */
return RT_EOK;
}
static rt_err_t _wdt_control(rt_watchdog_t *wdt, int cmd, void *arg)
{
switch (cmd)
{
/* feed the watchdog */
case RT_DEVICE_CTRL_WDT_KEEPALIVE:
/* Prevention of unexpected start-up when feed dog */
if (hc32_wdt.sta == WDT_IS_ENABLE)
{
WDT_FeedDog();
}
break;
/* set watchdog timeout */
case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
hc32_wdt.index = wdt_match_find_index((*((rt_uint32_t *)arg)));
hc32_wdt.stcwdg.u32CountPeriod = wdt_match_find_period(wdt_match[hc32_wdt.index].u32CountPeriod);
hc32_wdt.stcwdg.u32ClockDiv = ((uint32_t)log2(wdt_match[hc32_wdt.index].u32ClockDiv) << WDT_CR_CKS_POS);
if (WDT_Init(&hc32_wdt.stcwdg) != LL_OK)
{
LOG_E("wdg set timeout failed.");
return -RT_ERROR;
}
hc32_wdt.sta = WDT_INIT_OVER;
LOG_D("wdg set timeout successful. timeout = %d s", wdt_get_timeout_s());
break;
case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
(*((rt_uint32_t *)arg)) = wdt_get_timeout_s();
break;
case RT_DEVICE_CTRL_WDT_START:
if (hc32_wdt.sta == WDT_INIT_ING)
{
LOG_E("please set the timeout values.");
return -RT_ERROR;
}
/* First reload counter to start WDT */
WDT_FeedDog();
hc32_wdt.sta = WDT_IS_ENABLE;
break;
case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
(*((rt_uint32_t *)arg)) = wdt_get_timeleft_s();
break;
default:
LOG_W("This command is not supported.");
return -RT_ERROR;
}
return RT_EOK;
}
int rt_wdt_init(void)
{
_ops.init = &_wdt_init;
_ops.control = &_wdt_control;
hc32_wdt.watchdog.ops = &_ops;
/* register watchdog device */
if (rt_hw_watchdog_register(&hc32_wdt.watchdog, "wdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
{
LOG_E("wdt device register failed.");
return -RT_ERROR;
}
LOG_D("wdt device register success.");
return RT_EOK;
}
INIT_BOARD_EXPORT(rt_wdt_init);
#else /* BSP_USING_WDT */
struct hc32_swdt_obj
{
rt_watchdog_t watchdog;
stc_swdt_init_t stcwdg;
rt_uint32_t swdtclk;
rt_uint8_t sta;
rt_uint8_t index;
};
static struct hc32_swdt_obj hc32_swdt;
struct time_match
{
uint32_t u32ClockDiv;
uint32_t u32CountPeriod;
float timeout_s;
};
static uint32_t const Div[] = {1U, 16U, 32U, 64U, 128U, 256U, 2048U};
static uint32_t const Peri[] = {256U, 4096U, 16384U, 65536U};
static struct time_match swdt_match[(sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))];
static void swdt_match_init(uint32_t clock)
{
int i, j;
for (i = 0; i < (sizeof(Div) / sizeof(Div[0])); i++)
{
for (j = 0; j < (sizeof(Peri) / sizeof(Peri[0])); j++)
{
swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32ClockDiv = Div[i];
swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].u32CountPeriod = Peri[j];
swdt_match[j + i * (sizeof(Peri) / sizeof(Peri[0]))].timeout_s = (Div[i] * Peri[j]) / (float)clock;
}
}
}
static void swdt_match_sort(void)
{
int i, j;
struct time_match Temp;
/* bubble sort */
for (i = 0; i < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - 1); i++)
{
for (j = 0; j < ((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0])) - i - 1); j++)
{
if (swdt_match[j].timeout_s > swdt_match[j + 1].timeout_s)
{
memcpy(&Temp, &swdt_match[j], sizeof(struct time_match));
memcpy(&swdt_match[j], &swdt_match[j + 1], sizeof(struct time_match));
memcpy(&swdt_match[j + 1], &Temp, sizeof(struct time_match));
}
}
}
}
static int swdt_match_find_index(uint32_t time_out)
{
int i;
/* Min and Max case */
if (time_out <= swdt_match[0].timeout_s)
{
return 0;
}
else if (time_out >= swdt_match[((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1].timeout_s)
{
return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
}
/* Other case */
for (i = 1; i < (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1); i++)
{
if (time_out >= swdt_match[i].timeout_s && time_out < swdt_match[i + 1].timeout_s)
{
/* Min difference */
if (time_out - swdt_match[i].timeout_s < swdt_match[i + 1].timeout_s - time_out)
{
return i;
}
else
{
return (i + 1);
}
}
}
/* Not match case */
return (((sizeof(Div) / sizeof(Div[0])) * (sizeof(Peri) / sizeof(Peri[0]))) - 1);
}
static rt_uint32_t swdt_match_find_period(rt_uint32_t Period)
{
rt_uint32_t CountPeriod = 0U;
switch (Period)
{
case 256U:
CountPeriod = SWDT_CNT_PERIOD256;
break;
case 4096U:
CountPeriod = SWDT_CNT_PERIOD4096;
break;
case 16384U:
CountPeriod = SWDT_CNT_PERIOD16384;
break;
case 65536U:
CountPeriod = SWDT_CNT_PERIOD65536;
break;
default:
break;
}
return CountPeriod;
}
static rt_uint32_t swdt_get_timeout_s(void)
{
/* timeout(s) = PERI * DIV / SWDTCLK */
return ((rt_uint32_t)(swdt_match[hc32_swdt.index].u32CountPeriod * swdt_match[hc32_swdt.index].u32ClockDiv / (float)hc32_swdt.swdtclk));
}
static rt_uint32_t swdt_get_timeleft_s(void)
{
/* swdt is down counter */
return ((rt_uint32_t)(SWDT_GetCountValue() * swdt_match[hc32_swdt.index].u32ClockDiv / (float)hc32_swdt.swdtclk));
}
static rt_err_t swdt_init(rt_watchdog_t *swdt)
{
hc32_swdt.swdtclk = 10000U;
swdt_match_init(hc32_swdt.swdtclk);
swdt_match_sort();
hc32_swdt.stcwdg.u32RefreshRange = SWDT_RANGE_0TO100PCT;
#ifdef BSP_WDT_CONTINUE_COUNT
hc32_swdt.stcwdg.u32LPMCount = SWDT_LPM_CNT_CONTINUE;
#else
hc32_swdt.stcwdg.u32LPMCount = SWDT_LPM_CNT_STOP;
#endif
hc32_swdt.stcwdg.u32ExceptionType = SWDT_EXP_TYPE_RST;
hc32_swdt.sta = WDT_INIT_ING;
/* SWDT_CR register only support write once,so can't call swdt_Init of ther */
return RT_EOK;
}
static rt_err_t swdt_control(rt_watchdog_t *swdt, int cmd, void *arg)
{
switch (cmd)
{
/* feed the watchdog */
case RT_DEVICE_CTRL_WDT_KEEPALIVE:
/* Prevention of unexpected start-up when feed dog */
if (hc32_swdt.sta == WDT_IS_ENABLE)
{
SWDT_FeedDog();
}
break;
/* set watchdog timeout */
case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
hc32_swdt.index = swdt_match_find_index((*((rt_uint32_t *)arg)));
hc32_swdt.stcwdg.u32CountPeriod = swdt_match_find_period(swdt_match[hc32_swdt.index].u32CountPeriod);
hc32_swdt.stcwdg.u32ClockDiv = ((uint32_t)log2(swdt_match[hc32_swdt.index].u32ClockDiv) << SWDT_CR_CKS_POS);
if (SWDT_Init(&hc32_swdt.stcwdg) != LL_OK)
{
LOG_E("swdg set timeout failed.");
return -RT_ERROR;
}
hc32_swdt.sta = WDT_INIT_OVER;
LOG_D("swdg set timeout successful. timeout = %d s", swdt_get_timeout_s());
break;
case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
(*((rt_uint32_t *)arg)) = swdt_get_timeout_s();
break;
case RT_DEVICE_CTRL_WDT_START:
if (hc32_swdt.sta == WDT_INIT_ING)
{
LOG_E("please set the timeout values.");
return -RT_ERROR;
}
/* First reload counter to start swdt */
SWDT_FeedDog();
hc32_swdt.sta = WDT_IS_ENABLE;
break;
case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
(*((rt_uint32_t *)arg)) = swdt_get_timeleft_s();
break;
default:
LOG_W("This command is not supported.");
return -RT_ERROR;
}
return RT_EOK;
}
static int rt_hw_swdt_init(void)
{
_ops.init = &swdt_init;
_ops.control = &swdt_control;
hc32_swdt.watchdog.ops = &_ops;
/* register watchdog device */
if (rt_hw_watchdog_register(&hc32_swdt.watchdog, "swdt", RT_DEVICE_FLAG_DEACTIVATE, RT_NULL) != RT_EOK)
{
LOG_E("swdt device register failed.");
return -RT_ERROR;
}
LOG_D("swdt device register success.");
return RT_EOK;
}
INIT_BOARD_EXPORT(rt_hw_swdt_init);
#endif /* BSP_USING_SWDT */
#endif