基于S3C2440的嵌入式Linux驅動——看門狗(watchdog)驅動解讀
本文將介紹看門狗驅動的實現(xiàn)。
目標平臺:TQ2440
CPU:s3c2440
內核版本:2.6.30
看門狗其實就是一個定時器,當該定時器溢出前必須對看門狗進行"喂狗“,如果不這樣做,定時器溢出后則將復位CPU。
因此,看門狗通常用于對處于異常狀態(tài)的CPU進行復位。
具體的概念請自行百度。
2. S3C2440看門狗s3c2440的看門狗的原理框圖如下:
可以看出,看門狗定時器的頻率由PCLK提供,其預分頻器最大取值為255+1;另外,通過MUX,可以進一步降低頻率。
定時器采用遞減模式,一旦到0,則可以觸發(fā)看門狗中斷以及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) {