Watchdog
Watchdog启动之后,系统会在一定时间间隔后重启,这样可以防止系统在遇到死机或者突然崩溃时无法继续运行。若系统死机或者突然崩溃,在一定时间过后,Watchdog会重启系统,使系统恢复运行。
在正常的系统运行过程中,Watchdog若一直不断重启系统,会严重影响到系统的正常工作,因此需要一个程序在后台喂狗,防止系统重启,这样watchdog只有在系统崩溃,喂狗程序无法正常工作的时候重启系统。
Watchdog超时之后,会发送一个内部系统重启信号WDOG_RESET_B_DEB,给SRC(System RestController)。
Watchdog的功能:
1、Timeout event:用户可以设定timeout的时间,通过写Watchdog Control Register的WT(watchdog timeoutfield),设定timeout。当Watchdog enable 之后,watchdog 会读取WT中的timeout时间,counter开始倒计时,当counter为0时,会发送WDOG_RESET_B_DEB,使watchdog重启。
当timeout被设定之后,在counter为0之前,可以通过reload the counter,来重置counter的计时器时间。reload the counter的方法是向watchdog service register(WDOG_WSR)中先写入值0x5555,然后写入值0xAAAA(这两个值必须连续写入,必须先写入0x5555,否则counter是不会被reload的,watchdog 也会在counter计时为0时重启系统)。(寄存器地址以及功能可在spec中查到)。
后台喂狗程序就是通过不断reload counter来实现的。
2、Interrupt event:watchdog 可以产生一个irq,当timeout快发生的时候,IRQ的产生时间可以通过读写WDOG_WICR来控制。
一段简单的喂狗代码:
while(1) {
ret = ioctl(fd, WDIOC_KEEPALIVE, 0);//通过ioctl来与dirver进行通信
if (ret != 0) {
printf("Feed watchdog failed. \n");
close(fd);
return -1;
} else {
printf("Feed watchdog every %d seconds.\n", sleep_time);
}
sleep(sleep_time);//sleep_time 是喂狗的时间间隔
代码通过ioctl来传递参数给底层的driver,下面看一下WDIOC_KEEPALIVE在driver中的代表的操作:
case WDIOC_KEEPALIVE:
if (!(wdd->info->options & WDIOF_KEEPALIVEPING))
return -EOPNOTSUPP;
watchdog_ping(wdd);//wdd is watchdog device
在这个case语句中会执行watchdog_ping().这个函数就是执行reload counter的操作的,下面是源代码:
static int watchdog_ping(struct watchdog_device *wddev)
{
int err = 0;
mutex_lock(&wddev->lock);
if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
err = -ENODEV;
goto out_ping;
}
if (!watchdog_active(wddev))
goto out_ping;
if (wddev->ops->ping) //若dev中有定义ping的ops则pingwatchdog
err = wddev->ops->ping(wddev); /* ping the watchdog */
else
err = wddev->ops->start(wddev); /* restart watchdog */
out_ping:
mutex_unlock(&wddev->lock);
return err;
}
上述代码中:若在驱动中的ops实现了ping函数的功能,则执行ops中的ping函数。
进入到driver看ops中定义的ping函数:
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdt_start,
.stop = wdt_stop,
.ping = wdt_ping,
.set_timeout = wdt_set_timeout,
.set_pretimeout = wdt_set_pretimeout,
};
ping的函数为wdt_ping();查看wdt_ping()函数:
static int wdt_ping(struct watchdog_device *wdog)
{
struct wdt_device *wdev = watchdog_get_drvdata(wdog);
regmap_write(wdev->regmap, WDT_WSR, WDT_SEQ1);
regmap_write(wdev->regmap, WDT_WSR, WDT_SEQ2);
return 0;
}
代码中WDT_SEQ1宏定义为0x5555,WDT_SEQ2宏定义为0xAAAA
函数中通过先后连续向寄存器中写0x5555和0xAAAA,来reload counter,阻止watchdog发生timeout事件