- 我的博客:https://blog.csdn.net/qq_37388044
- 我的知乎:https://www.zhihu.com/people/bbtganmin
- 联系方式:知乎私信
转载或者引用本文内容请注明来源及原作者!
前言
一、原理
WatchDog 的运行基于 1 个 32bit 减法计数器,计数初值由寄存器 WDG_LOAD 载入。 在 WatchDog 时钟使能情况下,计数值在每个计数时钟的上升沿减 1。当计数值递减到 0,WatchDog 将产生一个中断。然后在下一个计数时钟上升沿,计数器又从寄存器 WDG_LOAD 中重新载入计数初值,开始递减计数。
如果计数器的计数值第二次计数递减到 0 时,CPU 还没有清除 WatchDog 中断,则 WatchDog 将发出复位信号 WDG_RSTN,计数器停止计数。
- WatchDog 计数时钟为 3MHz。
- 32bit 计数器最大值为 0xFFFFFFFF。
所以 WatchDog 的计数时间最大值为 1431s。
二、代码
头文件
#ifndef __BBT_WATCHDOG_H
#define __BBT_WATCHDOG_H
#include <asm/io.h>
#include <sys/types.h>
#include <linux/kernel.h>
void hi_watchdog_init(void);
void hi_feed_dog(void);
int hi_dog_set_heartbeat(int t);
#endif
c文件
#define DOG_UNLOCK_DATA 0x1ACCE551
#define DOG_BASE 0x20040000
#define WDG_LOAD (DOG_BASE + 0x0000)
#define WDG_VALUE (DOG_BASE + 0x0004)
#define WDG_CONTROL (DOG_BASE + 0x0008)
#define WDG_INTCLR (DOG_BASE + 0x000C)
#define WDG_RIS (DOG_BASE + 0x0010)
#define WDG_MIS (DOG_BASE + 0x0014)
#define WDG_LOCK (DOG_BASE + 0x0C00)
#define SC_CTRL (0x20050000 + 0x0)
#define DOG_CLK (3*1000*1000) //WatchDog 计数时钟为 3MHz
static inline void hi_dog_set_timeout(uint32_t nr)
{
uint32_t cnt = 1431; //先赋值最大值
unsigned long flags;
if( nr==0 || nr>cnt)
cnt = 0xffffffff;
else
cnt = nr*DOG_CLK;
spin_lock_irqsave(&hidog_lock, flags);
writel(DOG_UNLOCK_DATA, WDG_LOCK); //打开所有 WatchDog 寄存器的写权限
writel(cnt, WDG_LOAD); //初值
writel(cnt, WDG_VALUE); //当前值
writel(0, WDG_LOCK); //写入其他任何值,可以关闭所有 WatchDog 寄存器( WDG_LOCK 寄存器除外) 的写权限
spin_unlock_irqrestore(&hidog_lock, flags);
};
static inline void hi_dog_feed(void)
{
unsigned long flags;
spin_lock_irqsave(&hidog_lock, flags);
writel(DOG_UNLOCK_DATA, WDG_LOCK);
writel(0x00, WDG_INTCLR); //写任意值, WatchDog 清中断
writel(0, WDG_LOCK);
spin_unlock_irqrestore(&hidog_lock, flags);
};
int hi_dog_set_heartbeat(int t)
{
static int cur_margin = 10;
int ret = 0;
uint32_t cnt = 1431;
if( t==0 ) {
dprintf("将heartbeat设置为0,heartbeat将不会更改\n");
t = cur_margin;
ret = 1;
} else if( t>cnt ) {
dprintf("设置心跳范围错误, t = %d\n", t);
dprintf("force heartbeat to %d\n", cnt);
t = cnt;
ret = -1;
}
cur_margin = t;
hi_dog_set_timeout(t);
hi_dog_feed();
return ret;
}
static void hi_dog_start(void)
{
unsigned long flags;
spin_lock_irqsave(&hidog_lock, flags);
writel(DOG_UNLOCK_DATA, WDG_LOCK);
writel(0x00, WDG_CONTROL); //禁止(复位信号输出使能) 计数器停止计数,计数值保持当前值不变, WatchDog 被关闭
writel(0x00, WDG_INTCLR);
writel(0x03, WDG_CONTROL); //使能(复位信号输出使能) 既启动计数器又使能中断, WatchDog 被启动
writel(0, WDG_LOCK);
spin_unlock_irqrestore(&hidog_lock, flags);
}
static inline void hi_dog_stop(void)
{
unsigned long flags;
spin_lock_irqsave(&hidog_lock, flags);
writel(DOG_UNLOCK_DATA, WDG_LOCK);
writel(0x00, WDG_CONTROL);
writel(0x00, WDG_INTCLR);
writel(0, WDG_LOCK);
spin_unlock_irqrestore(&hidog_lock, flags);
hi_dog_set_timeout(0);
}
#if 0
static void watchdog_deamon(void) //守护线程
{
while(1)
{
hi_dog_feed();
sleep(8);
}
}
static void hi_watchdog_run(void)
{
TSK_INIT_PARAM_S stappTask;
int g_task = -1;
memset(&stappTask, 0, sizeof(TSK_INIT_PARAM_S));
stappTask.pfnTaskEntry = (TSK_ENTRY_FUNC)watchdog_deamon;
stappTask.uwStackSize = 0x800;
stappTask.pcName = "watchdog_deamon";
stappTask.usTaskPrio = 4;
stappTask.uwResved = LOS_TASK_STATUS_DETACHED;
LOS_TaskLock();
LOS_TaskCreate((uint32_t *)&g_task, &stappTask);
LOS_TaskUnlock();
}
#endif
void hi_watchdog_init(void)
{
hi_dog_start();
hi_dog_set_heartbeat(10);
}
void hi_feed_dog(void)
{
hi_dog_feed();
}