S3C2440上看門狗(Watchdog)驅(qū)動開發(fā)實例講解
嵌入式Linux之我行,主要講述和總結(jié)了本人在學(xué)習(xí)嵌入式linux中的每個步驟。一為總結(jié)經(jīng)驗,二希望能給想入門嵌入式Linux的朋友提供方便。如有錯誤之處,謝請指正。
共享資源,歡迎轉(zhuǎn)載:http://hbhuanggang.cublog.cn
一、開發(fā)環(huán)境
主 機:VMWare--Fedora 9
開發(fā)板:Mini2440--64MB Nand, Kernel:2.6.30.4
編譯器:arm-linux-gcc-4.3.2
二、相關(guān)概念
1、平臺設(shè)備及平臺設(shè)備驅(qū)動:
這個在前面篇幅:S3C2440上RTC時鐘驅(qū)動開發(fā)實例講解中已經(jīng)講過了。這里只需了解一下系統(tǒng)為我們定義的看門狗(Watchdog)平臺設(shè)備及資源情況,在arch/arm/plat-s3c24xx/devs.c中,如下:
/* Watchdog */
/*定義了Watchdog平臺設(shè)備使用的資源,這些資源在驅(qū)動程序中都會用到*/
static struct resource s3c_wdt_resource[] = {
[0] = { /*Watchdog所使用IO端口資源范圍*/
.start = S3C24XX_PA_WATCHDOG,
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM,
},
[1] = {/*Watchdog中斷資源*/
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
};
/*定義了Watchdog平臺設(shè)備*/
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt", /*設(shè)備名稱*/
.id = -1,
.num_resources= ARRAY_SIZE(s3c_wdt_resource), /*資源數(shù)量*/
.resource = s3c_wdt_resource, /*引用上面定義的資源*/
};
EXPORT_SYMBOL(s3c_device_wdt);
2、混雜設(shè)備(misc設(shè)備)
misc設(shè)備是Linux定義的主設(shè)備號為10的特殊字符設(shè)備,因為不符合字符設(shè)備的范疇,所以被歸納為misc設(shè)備,在Linux中有很多這種設(shè)備,例如:LED設(shè)備、Watchdog設(shè)備等等,系統(tǒng)會根據(jù)設(shè)備的次設(shè)備號來區(qū)分具體是哪個設(shè)備,通常這些次設(shè)備號被定義在include/linux/miscdevice.h中。在Linux中用miscdevice結(jié)構(gòu)體來描述一個misc設(shè)備,這就意味著被定義為misc設(shè)備的驅(qū)動中就要實現(xiàn)該結(jié)構(gòu)體中的接口函數(shù)。該結(jié)構(gòu)體也定義在miscdevice.h中,如下:
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
};
三、實例講解
1、Watchdog硬件結(jié)構(gòu)圖分析:
我們從結(jié)構(gòu)圖和數(shù)據(jù)手冊得知,看門狗Watchdog主要是實現(xiàn)系統(tǒng)自動復(fù)位的功能,他是利用芯片內(nèi)部的定時器,定時輸出連接到電路的復(fù)位端,程序在一定時間范圍內(nèi)對定時器清零(俗稱“喂狗”),所以程序在正常工作時,定時器總是不能溢出,也就不能產(chǎn)生復(fù)位信號;如果程序出現(xiàn)錯誤,不在定時周期內(nèi)復(fù)位看門狗,那么定時器就會溢出而產(chǎn)生復(fù)位信號使系統(tǒng)復(fù)位。
S3C2440的Watchdog模塊提供了三個寄存器來對Watchdog進行操作,他們分別是:定時器控制寄存器WTCON、定時器數(shù)據(jù)寄存器WTDAT和定時器計數(shù)寄存器WTCNT。注意:在對定時器數(shù)據(jù)寄存器WTDAT進行操作時必須在定時器控制寄存器WTCON使能之前寫入一個計數(shù)目標(biāo)值,當(dāng)Watchdog使能開啟后,WTDAT中的值會自動被加載到計數(shù)寄存器WTCNT中,然后Watchdog從CPU內(nèi)部的時鐘分頻和時鐘除數(shù)因子得到一個工作周期,當(dāng)每個周期結(jié)束時計數(shù)寄存器WTCNT中的值會1,直到遞減為0時,如果還不重新往WTCNT中寫入新的計數(shù)目標(biāo)值(即“喂狗”),則Watchdog就產(chǎn)生復(fù)位信號使系統(tǒng)復(fù)位。關(guān)于這些寄存器的功能和寄存器的各個位的操作值請參考數(shù)據(jù)手冊。
2、Watchdog驅(qū)動程序具體實現(xiàn)步驟(建立驅(qū)動文件my2440_watchdog.c):
注意:在每步中,為了讓代碼邏輯更加有條理和容易理解,就沒有考慮代碼的順序,比如函數(shù)要先定義后調(diào)用。如果要編譯此代碼,請嚴(yán)格按照C語言的規(guī)范來調(diào)整代碼的順序。
①、依然是驅(qū)動程序的最基本結(jié)構(gòu):Watchdog驅(qū)動的初始化和卸載部分及其他,如下:
#include
#include
#include
#include
/*Watchdog平臺驅(qū)動結(jié)構(gòu)體,平臺驅(qū)動結(jié)構(gòu)體定義在platform_device.h中,該結(jié)構(gòu)體內(nèi)的接口函數(shù)在第②、④步中實現(xiàn)*/
static struct platform_driver watchdog_driver =
{
.probe= watchdog_probe,/*Watchdog探測函數(shù),在第②步中實現(xiàn)*/
.remove= __devexit_p(watchdog_remove),/*Watchdog移除函數(shù), 在第④步中實現(xiàn)*/
.shutdown= watchdog_shutdown,/*Watchdog關(guān)閉函數(shù),在第④步中實現(xiàn)*/
.suspend= watchdog_suspend,/*Watchdog掛起函數(shù),在第④步中實現(xiàn)*/
.resume= watchdog_resume,/*Watchdog恢復(fù)函數(shù),在第④步中實現(xiàn)*/
.driver=
{
/*注意這里的名稱一定要和系統(tǒng)中定義平臺設(shè)備的地方一致,這樣才能把平臺設(shè)備與該平臺設(shè)備的驅(qū)動關(guān)聯(lián)起來*/
.name= "s3c2410-wdt",
.owner= THIS_MODULE,
},
};
static int __init watchdog_init(void)
{
/*將Watchdog注冊成平臺設(shè)備驅(qū)動*/
return platform_driver_register(&watchdog_driver);
}
static void __exit watchdog_exit(void)
{
/*注銷Watchdog平臺設(shè)備驅(qū)動*/
platform_driver_unregister(&watchdog_driver);
}
module_init(watchdog_init);
module_exit(watchdog_exit);
/*驅(qū)動程序模塊參數(shù),如果在加載驅(qū)動模塊時沒有設(shè)定這些參數(shù),則這些參數(shù)將采用默認(rèn)值,
這些參數(shù)在接下來的步驟中將被一一用到,參數(shù)具體作用也將在各步驟中來說明*/
module_param(tmr_margin, int, 0);
module_param(tmr_atboot, int, 0);
module_param(nowayout, int, 0);
module_param(soft_noboot,int, 0);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("My2440 Watchdog Driver");
②、Watchdog平臺驅(qū)動結(jié)構(gòu)中探測函數(shù)watchdog_probe的實現(xiàn)。探測就意味著在系統(tǒng)總線中去檢測設(shè)備的存在,然后獲取設(shè)備有用的相關(guān)資源信息,以便我們使用這些信息。代碼如下:
#include
#include
#include
#include
#include
#include
/*定義了一個用來保存watchdog的IO端口占用的IO空間和經(jīng)過虛擬映射后的內(nèi)存地址*/
static struct resource *wdt_mem;
static void __iomem *wdt_base;
/*保存watchdog中斷號,NO_IRQ宏定義在irq.h中*/
static int wdt_irqno = NO_IRQ;
/*保存從平臺時鐘隊列中獲取watchdog的時鐘*/
static struct clk *wdt_clock;
#define CONFIG_WATCHDOG_ATBOOT(0)
#define CONFIG_WATCHDOG_DEFAULT_TIME(15)
static int tmr_atboot = CONFIG_WATCHDOG_ATBOOT;
static int tmr_margin = CONFIG_WATCHDOG_DEFAULT_TIME;
static int soft_noboot;
static unsigned int wdt_count;/*用于保存經(jīng)計算后得到的計數(shù)寄存器WTCNT的計數(shù)值*/
/*申明并初始化一個自旋鎖wdt_pie_lock,對Watchdog資源進行互斥訪問*/
static DEFINE_SPINLOCK(wdt_pie_lock);
static int __devinit watchdog_probe(struct platform_device *pdev)
{
int ret;
int started = 0;
struct resource *res;/*定義一個資源,用來保存獲取的watchdog的IO資源*/
/*在系統(tǒng)定義的watchdog平臺設(shè)備中獲取watchdog中斷號
platform_get_irq定義在platform_device.h中*/
wdt_irqno = platform_get_irq(pdev, 0);
if(wdt_irqno < 0)
{
/*獲取watchdog中斷號不成功錯誤處理
dev_err定義在device.h中,在platform_device.h中已經(jīng)引用,所以這里就不需再引用了*/
dev_err(&pdev->dev, "no irq for watchdogn");
return -ENOENT;
}
/*申請Watchdog中斷服務(wù),這里使用的是快速中斷:IRQF_DISABLED。
中斷服務(wù)程序為:wdt_irq,將Watchdog平臺設(shè)備pdev做參數(shù)傳遞過去了*/
ret = request_irq(wdt_irqno, wdt_irq, IRQF_DISABLED, pdev->name, pdev);
if(ret)
{
/*錯誤處理*/
dev_err(dev, "IRQ%d error %dn", wdt_irqno, ret);
return ret;