基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
扫描二维码
随时随地手机看文章
本文将介绍看门狗驱动的实现。
目标平台:TQ2440
CPU:s3c2440
内核版本:2.6.30
看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行"喂狗“,如果不这样做,定时器溢出后则将复位CPU。
因此,看门狗通常用于对处于异常状态的CPU进行复位。
具体的概念请自行百度。
2. S3C2440看门狗s3c2440的看门狗的原理框图如下:
可以看出,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。
定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。
看门狗定时器的频率的计算公式如下:
看门狗驱动代码位于:linux/drivers/char/watchdog/s3c2410_wdt.c
staticstructplatform_drivers3c2410wdt_driver={
.probe=s3c2410wdt_probe,
.remove=s3c2410wdt_remove,
.shutdown=s3c2410wdt_shutdown,
.suspend=s3c2410wdt_suspend,
.resume=s3c2410wdt_resume,
.driver={
.owner=THIS_MODULE,
.name="s3c2410-wdt",
},
};
staticcharbanner[]__initdata=
KERN_INFO"S3C2410WatchdogTimer,(c)2004SimtecElectronicsn";
staticint__initwatchdog_init(void){printk(banner);returnplatform_driver_register(&s3c2410wdt_driver);}
module_init(watchdog_init)
模块的注册函数很简单,直接调用了 platform的驱动注册函数platform_driver_register。
该函数在注册时会调用驱动的probe方法,也即s3c2410wdt_probe函数。
我们来看下这个函数:
staticints3c2410wdt_probe(structplatform_device*pdev)
{
structresource*res;
structdevice*dev;
unsignedintwtcon;
intstarted=0;
intret;
intsize;
DBG("%s:probe=%pn",__func__,pdev);
dev=&pdev->dev;
wdt_dev=&pdev->dev;
/*getthememoryregionforthewatchdogtimer*/
/*获取平台资源,寄存器地址范围*/
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(res==NULL){
dev_err(dev,"nomemoryresourcespecifiedn");
return-ENOENT;
}
/*内存申请*/
size=(res->end-res->start)+1;
wdt_mem=request_mem_region(res->start,size,pdev->name);
if(wdt_mem==NULL){
dev_err(dev,"failedtogetmemoryregionn");
ret=-ENOENT;
gotoerr_req;
}
/*内存映射*/
wdt_base=ioremap(res->start,size);
if(wdt_base==NULL){
dev_err(dev,"failedtoioremap()regionn");
ret=-EINVAL;
gotoerr_req;
}
DBG("probe:mappedwdt_base=%pn",wdt_base);
/*获取平台资源,看门狗定时器中断号*/
wdt_irq=platform_get_resource(pdev,IORESOURCE_IRQ,0);
if(wdt_irq==NULL){
dev_err(dev,"noirqresourcespecifiedn");
ret=-ENOENT;
gotoerr_map;
}
/*注册看门狗定时器中断*/
ret=request_irq(wdt_irq->start,s3c2410wdt_irq,0,pdev->name,pdev);
if(ret!=0){
dev_err(dev,"failedtoinstallirq(%d)n",ret);
gotoerr_map;
}
/*获取看门狗模块的时钟*/
wdt_clock=clk_get(&pdev->dev,"watchdog");
if(IS_ERR(wdt_clock)){
dev_err(dev,"failedtofindwatchdogclocksourcen");
ret=PTR_ERR(wdt_clock);
gotoerr_irq;
}
/*使能该时钟*/
clk_enable(wdt_clock);
/*seeifwecanactuallysettherequestedtimermargin,andif
*not,trythedefaultvalue*/
/*设置定时器模块的时钟频率*/
if(s3c2410wdt_set_heartbeat(tmr_margin)){
started=s3c2410wdt_set_heartbeat(
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if(started==0)
dev_info(dev,
"tmr_marginvalueoutofrange,default%dusedn",
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
else
dev_info(dev,"defaulttimervalueisoutofrange,cannotstartn");
}
/*注册混杂设备,设备名watchdog,次设备号130*/
ret=misc_register(&s3c2410wdt_miscdev);
if(ret){
dev_err(dev,"cannotregistermiscdevonminor=%d(%d)n",
WATCHDOG_MINOR,ret);
gotoerr_clk;
}
/*
*如果需要在看门狗模块加载时启动看门狗则
*调用s3c2410wdt_start,否则调用s3c2410wdt_stop
*/
if (tmr_atboot && started == 0) {