基于S3C2440的嵌入式Linux驅(qū)動——看門狗(watchdog)驅(qū)動解讀
本文將介紹看門狗驅(qū)動的實(shí)現(xiàn)。
目標(biāo)平臺:TQ2440
CPU:s3c2440
內(nèi)核版本:2.6.30
看門狗其實(shí)就是一個定時器,當(dāng)該定時器溢出前必須對看門狗進(jìn)行"喂狗“,如果不這樣做,定時器溢出后則將復(fù)位CPU。
因此,看門狗通常用于對處于異常狀態(tài)的CPU進(jìn)行復(fù)位。
具體的概念請自行百度。
2. S3C2440看門狗s3c2440的看門狗的原理框圖如下:
可以看出,看門狗定時器的頻率由PCLK提供,其預(yù)分頻器最大取值為255+1;另外,通過MUX,可以進(jìn)一步降低頻率。
定時器采用遞減模式,一旦到0,則可以觸發(fā)看門狗中斷以及RESET復(fù)位信號。
看門狗定時器的頻率的計(jì)算公式如下:
看門狗驅(qū)動代碼位于: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)
模塊的注冊函數(shù)很簡單,直接調(diào)用了 platform的驅(qū)動注冊函數(shù)platform_driver_register。
該函數(shù)在注冊時會調(diào)用驅(qū)動的probe方法,也即s3c2410wdt_probe函數(shù)。
我們來看下這個函數(shù):
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;
}
/*內(nèi)存申請*/
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;
}
/*內(nèi)存映射*/
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*/
/*設(shè)置定時器模塊的時鐘頻率*/
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");
}
/*注冊混雜設(shè)備,設(shè)備名watchdog,次設(shè)備號130*/
ret=misc_register(&s3c2410wdt_miscdev);
if(ret){
dev_err(dev,"cannotregistermiscdevonminor=%d(%d)n",
WATCHDOG_MINOR,ret);
gotoerr_clk;
}
/*
*如果需要在看門狗模塊加載時啟動看門狗則
*調(diào)用s3c2410wdt_start,否則調(diào)用s3c2410wdt_stop
*/
if (tmr_atboot && started == 0) {