看门狗一般应用于系统异常或者核心应用异常,一定时间内发出复位信号,以复位整个系统。
我将分三个层次来说明:
1 寄存器层
2 驱动层
3 应用层
hi3516dv300有2个watchdog模块;
WatchDog 0 基址:0x1205_0000。
WatchDog 1 基址:0x1205_1000
watchdog寄存器概览
偏移地址 | 名称 | 描述 |
---|---|---|
0x0000 | WDG_LOAD | 计数初值寄存器 |
0x0004 | WDG_VALUE | 计数器当前值寄存器 |
0x0008 | WDG_CONTROL | 控制寄存器 |
0x000C | WDG_INTCLR | 中断清除寄存器 |
0x0010 | WDG_RIS | 原始中断寄存器 |
0x0014 | WDG_MIS | 屏蔽后中断寄存器 |
0x0C00 | WDG_LOCK | LOCK 寄存器 |
watchdog计数时钟为3MHz时钟。
time=val*1/fclk
time:watchdog计数时间
val:watchdog计数初值
fclk:watchdog计数时钟频率
watchdog的计数时间time范围为0s~1400s
系统上电复位后wat计数器处于停止计数状态。
收到watchdog发出的中断后,要清除其中断状态,并使其载入计数初值重新开始计数,该过程可以表述为喂狗。如果计数器的计数值第二次计数递减到0时,CPU还没有清除watchdog中断,则watchdog将发出复位信号WDG_RSTN,系统将重启。
写寄存器WDG_CONTROL[inten]控制位:
0: 关闭watchdog
1:开启watchdog
这里我不贴出所有代码,sdk中有该驱动源码,位置为Hi3516CV500_SDK_V2.0.1.1/smp/a7_linux/drv/interdrv/wdt/hi_wdt.c
/* define watchdog IO */
#define HIWDT_BASE 0x12051000
#define HIWDT_REG(x) (HIWDT_BASE + (x))
#define HIWDT_LOAD 0x000
#define HIWDT_VALUE 0x004
#define HIWDT_CTRL 0x008
#define HIWDT_INTCLR 0x00C
#define HIWDT_RIS 0x010
#define HIWDT_MIS 0x014
#define HIWDT_LOCK 0xC00
#define HIWDT_UNLOCK_VAL 0x1ACCE551
#ifndef MHZ
#define MHZ (1000*1000)
#endif
static unsigned long rate = 3*MHZ;
// 设置喂狗超时时间
static inline void hidog_set_timeout(unsigned int nr)
{
unsigned long cnt = (~0x0UL)/rate; /* max cnt */
unsigned long flags;
osal_spin_lock_irqsave(&hidog_lock, &flags);
if( nr==0 || nr>cnt)
cnt=~0x0;
else
cnt = nr*rate;
/* unlock watchdog registers */
hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK);
hiwdt_writel(cnt, HIWDT_LOAD);
hiwdt_writel(cnt, HIWDT_VALUE);
/* lock watchdog registers */
hiwdt_writel(0, HIWDT_LOCK);
osal_spin_unlock_irqrestore(&hidog_lock, &flags);
};
// 喂狗
static inline void hidog_feed(void)
{
unsigned long flags;
/* read the RIS state of current wdg */
int v=0;
v = hiwdt_readl(HIWDT_RIS);
v &= 0x1;
if (0 == v) /*no INT on current wdg */
return;
osal_spin_lock_irqsave(&hidog_lock, &flags);
/* unlock watchdog registers */
hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK);
/* clear watchdog */
hiwdt_writel(0x00, HIWDT_INTCLR);
/* lock watchdog registers */
hiwdt_writel(0, HIWDT_LOCK);
osal_spin_unlock_irqrestore(&hidog_lock, &flags);
};
// 唤醒狗
static inline void hidog_start(void)
{
unsigned long flags;
//unsigned long t;
osal_spin_lock_irqsave(&hidog_lock, &flags);
/* unlock watchdog registers */
hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK);
hiwdt_writel(0x00, HIWDT_CTRL);
hiwdt_writel(0x00, HIWDT_INTCLR);
hiwdt_writel(0x03, HIWDT_CTRL);
/* lock watchdog registers */
hiwdt_writel(0, HIWDT_LOCK);
/* enable watchdog clock --- set the frequency to 3MHz */
//t = osal_readl(reg_ctl_base_va);
//osal_writel(t & ~0x00800000, reg_ctl_base_va);
osal_spin_unlock_irqrestore(&hidog_lock, &flags);
options = WDIOS_ENABLECARD;
}
// 关狗
static inline void hidog_stop(void)
{
unsigned long flags;
osal_spin_lock_irqsave(&hidog_lock, &flags);
/* unlock watchdog registers */
hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK);
/* stop watchdog timer */
hiwdt_writel(0x00, HIWDT_CTRL);
hiwdt_writel(0x00, HIWDT_INTCLR);
/* lock watchdog registers */
hiwdt_writel(0, HIWDT_LOCK);
osal_spin_unlock_irqrestore(&hidog_lock, &flags);
hidog_set_timeout(0);
options = WDIOS_DISABLECARD;
}
功能接口
/**
* @brief create watchdog task
* @param[in] s32TimeOut: feed dog time, [2, MAX]
* @return 0 success,non-zero error code.
*/
HI_S32 HI_HAL_WATCHDOG_Init(HI_S32 s32TimeOut);
/**
* @brief feed watchdog
* @return 0 success,non-zero error code.
*/
HI_S32 HI_HAL_WATCHDOG_Feed(HI_VOID);
/**
* @brief destroy watchdog task
* @return 0 success,non-zero error code.
*/
HI_S32 HI_HAL_WATCHDOG_Deinit(HI_VOID);
功能实现
#define HI_APPCOMM_MAX_PATH_LEN (64)
#define HAL_FD_INITIALIZATION_VAL (-1)
#define HAL_WATCHDOG_DEV "/dev/watchdog"
static HI_S32 s_s32HALWATCHDOGfd = HAL_FD_INITIALIZATION_VAL;
HI_S32 HI_WATCHDOG_Init(HI_S32 s32Time_s)
{
HI_S32 s32Ret = HI_SUCCESS;
if (s_s32HALWATCHDOGfd != HAL_FD_INITIALIZATION_VAL)
{
MLOGE("already init");
return HI_HAL_EINITIALIZED;
}
if (s32Time_s < 2 || s32Time_s > 1000)
{
MLOGE("Interval time should not be less then two and bigger then 100. %d\n", s32Time_s);
return HI_HAL_EINVAL;
}
HI_CHAR szWdtString[HI_APPCOMM_MAX_PATH_LEN] = {0};
snprintf(szWdtString, HI_APPCOMM_MAX_PATH_LEN, " default_margin=%d nodeamon=1", s32Time_s);
#if defined(HI3559V200)
s32Ret = HI_insmod(HI_APPFS_KOMOD_PATH"/hi3559v200_wdt.ko",szWdtString);
if(HI_SUCCESS != s32Ret)
{
MLOGE("insmod 3c3416: failed, errno(%d)\n", errno);
return HI_HAL_EINTER;
}
#elif defined(__HI3516DV300__)
s32Ret = HI_insmod("/komod/hi3516cv500_wdt.ko",NULL);
if(HI_SUCCESS != s32Ret && errno != EEXIST)
{
MLOGE("insmod hi3516cv500_wdt.ko: failed, errno(%d)\n", errno);
return HI_HAL_EINTER;
}
#endif
s_s32HALWATCHDOGfd = open(HAL_WATCHDOG_DEV, O_RDWR);
if (s_s32HALWATCHDOGfd < 0)
{
MLOGE("open [%s] failed\n",HAL_WATCHDOG_DEV);
return HI_HAL_EINVOKESYS;
}
#if defined(__HI3516DV300__ )
s32Ret = ioctl(s_s32HALWATCHDOGfd, WDIOC_SETTIMEOUT, &s32Time_s);
if(-1 == s32Ret)
{
MLOGE("WDIOC_SETTIMEOUT: failed, errno(%d)\n", errno);
return HI_HAL_EINTER;
}
MLOGI(BLUE"WDIOC_SETTIMEOUT: timeout=%ds\n"NONE, s32Time_s);
#endif
s32Ret = ioctl(s_s32HALWATCHDOGfd, WDIOC_KEEPALIVE);/**feed dog */
if(-1 == s32Ret)
{
MLOGE("WDIOC_KEEPALIVE: failed, errno(%d)\n", errno);
return HI_HAL_EINTER;
}
return HI_SUCCESS;
}
HI_S32 HI_HAL_WATCHDOG_Feed(HI_VOID)
{
HI_S32 s32Ret = HI_SUCCESS;
s32Ret = ioctl(s_s32HALWATCHDOGfd, WDIOC_KEEPALIVE);/**feed dog */
if(-1 == s32Ret)
{
MLOGE("WDIOC_KEEPALIVE: failed, errno(%d)\n", errno);
return HI_HAL_EINTER;
}
return HI_SUCCESS;
}
HI_S32 HI_HAL_WATCHDOG_Deinit(HI_VOID)
{
HI_S32 s32Ret;
if (s_s32HALWATCHDOGfd == HAL_FD_INITIALIZATION_VAL)
{
MLOGE("watchdog not initialized,no need to close\n");
return HI_HAL_ENOINIT;
}
s32Ret = close(s_s32HALWATCHDOGfd);
if (0 > s32Ret)
{
MLOGE("wdrfd[%d] close,fail,errno(%d)\n",s_s32HALWATCHDOGfd,errno);
return HI_HAL_EINVOKESYS;
}
s_s32HALWATCHDOGfd = HAL_FD_INITIALIZATION_VAL;
return HI_SUCCESS;
}