儘管在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 &
最後再重新編譯根檔案系統並燒寫到開發板上。此時系統雖然開啟了看門狗功能,但由於後台不斷在喂狗,所以系統仍然可以正常運行。