基于S3C2440的Linux-3.6.6移植——看门狗定时器的应用
尽管在linux系统中,对于S3C2440开发板来说,默认是已经配置了看门狗定时器,如:DeviceDrivers ---> [*] Watchdog Timer Support ---> S3C2410 Watchdog但看门狗定时器是没有打开的,所以我们会在启动系统的时候,看到如下信息提示:s3c2410-wdts3c2410-wdt: w
尽管在linux系统中,对于S3C2440开发板来说,默认是已经配置了看门狗定时器,如:
DeviceDrivers --->
[*] Watchdog Timer Support --->
<*> S3C2410 Watchdog
但看门狗定时器是没有打开的,所以我们会在启动系统的时候,看到如下信息提示:
s3c2410-wdts3c2410-wdt: watchdoginactive, reset disabled, irq disabled
下面就具体分析一下看门狗定时器。
在Mach-zhaocj2440.c文件中已经添加了看门狗定时器这个平台设备:
static struct platform_device *zhaocj2440_devices[]__initdata = {
……
&s3c_device_wdt,
……
};
而这个平台设备的具体定义是在arch/arm/plat-samsung目录下的Devs.c文件内给出的:
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
.resource = s3c_wdt_resource,
};
该平台设备所对应的平台驱动定义在drivers/watchdog目录下的S3c2410_wdt.c文件内:
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,
.remove = __devexit_p(s3c2410wdt_remove),
.shutdown = s3c2410wdt_shutdown,
.suspend = s3c2410wdt_suspend,
.resume = s3c2410wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",
.of_match_table = of_match_ptr(s3c2410_wdt_match),
},
};
在S3c2410_wdt.c文件内的开始处,定义了几个很重要的变量:
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
static int soft_noboot;
static int debug;
变量nowayout表示是否允许关闭看门狗定时器,1表示不允许,0表示允许;变量tmr_margin表示喂狗的最长时间间隔,上电默认的时间为CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME,即至少需要15秒钟为看门狗定时器赋值一次,否则定时器会溢出,系统会复位;变量tmr_atboot表示系统启动后是否使能看门狗,1表示使能,0表示关闭,由于CONFIG_S3C2410_WATCHDOG_ATBOOT为0,所以系统启动后看门狗是没有打开的,因此即使编译时配置了看门狗,也无需喂狗;变量soft_noboot表示是否把看门狗当做一般的定时器来用。
在S3c2410_wdt.c文件内,还定义了一个重要的结构:
static struct watchdog_device s3c2410_wdd = {
.info= &s3c2410_wdt_ident,
.ops= &s3c2410wdt_ops,
};
其中.ops指向的是s3c2410wdt_ops结构:
static struct watchdog_ops s3c2410wdt_ops = {
.owner= THIS_MODULE,
.start= s3c2410wdt_start,
.stop= s3c2410wdt_stop,
.ping= s3c2410wdt_keepalive,
.set_timeout= s3c2410wdt_set_heartbeat,
};
s3c2410wdt_start函数用于开启看门狗,s3c2410wdt_stop函数用于关闭看门狗,s3c2410wdt_keepalive函数用于喂狗,s3c2410wdt_set_heartbeat函数用于设置看门狗的定时时间间隔,即喂狗时间。
下面就重点介绍一下s3c2410wdt_probe函数:
static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
{
structdevice *dev;
unsignedint wtcon;
intstarted = 0;
intret;
intsize;
DBG("%s:probe=%p\n", __func__, pdev);
dev= &pdev->dev;
wdt_dev= &pdev->dev;
//获取看门狗平台设备所使用的内存映射空间
wdt_mem= platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(wdt_mem == NULL) {
dev_err(dev,"no memory resource specified\n");
return-ENOENT;
}
//获取看门狗平台设备所使用的中断号
wdt_irq= platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(wdt_irq == NULL) {
dev_err(dev,"no irq resource specified\n");
ret= -ENOENT;
gotoerr;
}
/*get the memory region for the watchdog timer */
//获取内存空间大小
size= resource_size(wdt_mem);
if(!request_mem_region(wdt_mem->start, size, pdev->name)) {
dev_err(dev,"failed to get memory region\n");
ret= -EBUSY;
gotoerr;
}
//获取内存基址
wdt_base= ioremap(wdt_mem->start, size);
if(wdt_base == NULL) {
dev_err(dev,"failed to ioremap() region\n");
ret= -EINVAL;
gotoerr_req;
}
DBG("probe:mapped wdt_base=%p\n", wdt_base);
//从平台时钟队列中获取看门狗的时钟
wdt_clock= clk_get(&pdev->dev, "watchdog");
if(IS_ERR(wdt_clock)) {
dev_err(dev,"failed to find watchdog clock source\n");
ret= PTR_ERR(wdt_clock);
gotoerr_map;
}
//使能看门狗时钟
clk_enable(wdt_clock);
//注册CPU频率
ret= s3c2410wdt_cpufreq_register();
if(ret < 0) {
pr_err("failedto register cpufreq\n");
gotoerr_clk;
}
/*see if we can actually set the requested timer margin, and if
* not, try the default value */
//设置看门狗定时器的溢出时间间隔
if(s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
started= s3c2410wdt_set_heartbeat(&s3c2410_wdd,
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if(started == 0)
dev_info(dev,
"tmr_margin value out of range, default%d used\n",
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
else
dev_info(dev,"default timer value is out of range, "
"cannotstart\n");
}
//申请看门狗定时中断,因为看门狗定时器也可以当一般的定时器中断来用
ret= request_irq(wdt_irq->start, s3c2410wdt_irq,0, pdev->name, pdev);
if(ret != 0) {
dev_err(dev,"failed to install irq (%d)\n", ret);
gotoerr_cpufreq;
}
//由nowayout的值设置看门狗的属性
watchdog_set_nowayout(&s3c2410_wdd, nowayout);
//注册看门狗设备
ret= watchdog_register_device(&s3c2410_wdd);
if(ret) {
dev_err(dev,"cannot register watchdog (%d)\n", ret);
gotoerr_irq;
}
//由tmr_atboot的值来决定是否开启看门狗定时器
if(tmr_atboot && started == 0) {
dev_info(dev,"starting watchdog timer\n");
s3c2410wdt_start(&s3c2410_wdd);
}else if (!tmr_atboot) {
/*if we're not enabling the watchdog, then ensure it is
* disabled if it has been left running fromthe bootloader
* or other source */
s3c2410wdt_stop(&s3c2410_wdd);
}
/*print out a statement of readiness */
//打印出看门狗的状态信息,即系统启动时显示的看门狗信息
wtcon= readl(wdt_base + S3C2410_WTCON);
dev_info(dev,"watchdog %sactive, reset %sabled, irq %sabled\n",
(wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
(wtcon & S3C2410_WTCON_RSTEN)? "en" : "dis",
(wtcon & S3C2410_WTCON_INTEN) ? "en" :"dis");
return0;
//错误异常处理
err_irq:
free_irq(wdt_irq->start,pdev);
err_cpufreq:
s3c2410wdt_cpufreq_deregister();
err_clk:
clk_disable(wdt_clock);
clk_put(wdt_clock);
wdt_clock= NULL;
err_map:
iounmap(wdt_base);
err_req:
release_mem_region(wdt_mem->start,size);
err:
wdt_irq= NULL;
wdt_mem= NULL;
returnret;
}
从上面的介绍中可以看出,s3c2410wdt_probe函数中最重要的部分是调用watchdog_register_device函数用以注册看门狗。watchdog_register_device函数在drivers/watchdog目录下的Watchdog_core.c文件内。而watchdog_register_device函数最重要的作用是调用watchdog_dev_register函数,它在drivers/watchdog目录下的Watchdog_dev.c文件内,即把看门狗设备注册成一个杂项设备。
看门狗定时器的杂项设备结构为:
static struct miscdevice watchdog_miscdev ={
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &watchdog_fops,
};
watchdog_fops结构为:
static const struct file_operationswatchdog_fops = {
.owner = THIS_MODULE,
.write = watchdog_write,
.unlocked_ioctl = watchdog_ioctl,
.open = watchdog_open,
.release = watchdog_release,
};
在打开看门狗的函数watchdog_open内,调用了watchdog_start函数,而在watchdog_start函数中最终是调用的S3c2410_wdt.c文件中的s3c2410wdt_start函数来实现看门狗的开启。在写看门狗函数函数watchdog_write内,调用了watchdog_ping函数,而在watchdog_ping函数中最终是调用的S3c2410_wdt.c文件中的s3c2410wdt_keepalive函数来实现喂狗。在控制看门狗函数watchdog_ioctl内,实现了对看门狗的不同操作,如写看门狗、获得看门狗的状态、设置看门狗的定时时间等。
下面我们就开启看门狗的功能,并写一段应用程序来实现喂狗以防止系统复位。
开启看门狗很简单,只需要在S3c2410_wdt.c文件内把CONFIG_S3C2410_WATCHDOG_ATBOOT改为1即可:
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (1)
重新编译Linux后,在启动系统的过程中,则会打印下列信息:
s3c2410-wdt s3c2410-wdt: starting watchdog timer
s3c2410-wdt s3c2410-wdt: watchdog active, reset enabled, irqdisabled
这时,如果我们不实现喂狗功能的话,系统就会不断的复位重启。
下面就是实现喂狗的应用程序:
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<linux/watchdog.h>
intmain(int argc, char **argv){
int fd;
fd = open("/dev/watchdog",O_RDONLY);
if(fd < 0){
perror("/dev/watchdog");
return -1;
}
for(;;){
ioctl(fd, WDIOC_KEEPALIVE);
sleep(5);
}
close(fd);
return 0;
}
我们把上面程序编译成wdt文件,然后把它复制到根文件系统的bin目录下,再修改根文件系统的etc/init.d/rcS文件,在该文件的最后添加wdt &一句,即实现了在后台喂狗的功能。下面是rcS文件的内容:
mount -a
mkdir/dev/pts
mount -tdevpts devpts /dev/pts
echo /sbin/mdev> /proc/sys/kernel/hotplug
mdev -s
wdt &
最后再重新编译根文件系统并烧写到开发板上。此时系统虽然开启了看门狗功能,但由于后台不断在喂狗,所以系统仍然可以正常运行。
更多推荐
所有评论(0)