日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

WatchDog驅(qū)動程序

 落寒影LIB 2014-09-22

看門狗是當(dāng)CPU進(jìn)入錯誤狀態(tài)后,無法恢復(fù)的情況下,使計算機(jī)重新啟動

由于計算機(jī)在工作時不可避免的受到各種各樣的因素干擾,即使再優(yōu)秀的計算機(jī)程序也可能因?yàn)檫@種干擾使計算機(jī)進(jìn)入一個死循環(huán),更嚴(yán)重的就是導(dǎo)致死機(jī)。

有兩種辦法來處理這種情況:

一是:采用人工復(fù)位的方法

二是:依賴于某種硬件來執(zhí)行這個復(fù)位工作。這種硬件通常叫做看門狗(Watch Dog,WD)

看門狗,就像一只狗一樣,在那看著們,計算機(jī)中通常用定時器來處理這種周期性的動作

看門狗實(shí)際上是一個定時器,其硬件內(nèi)部維護(hù)了一個定時器,每當(dāng)時鐘信號到來時,計數(shù)寄存器減1。如果減到0,則系統(tǒng)重啟(就像狗一樣,看你不認(rèn)識就咬你,可不管你是誰)。

如果在減到0之前,系統(tǒng)又設(shè)置計數(shù)寄存器一個較大的值,那么系統(tǒng)永遠(yuǎn)不會重啟。系統(tǒng)的這種設(shè)置能力表示系統(tǒng)一直處于一種正常運(yùn)行狀態(tài)。反之,如果計算機(jī)系統(tǒng)崩潰,那么就無法重新設(shè)置計數(shù)寄存器的值。當(dāng)計數(shù)寄存器為0,系統(tǒng)重啟

 

看門狗的工作原來很簡單,處理器內(nèi)部一般都集成了一個看門狗硬件。其提供了三個寄存器

看門狗控制寄存器(WTCON)

看門狗數(shù)據(jù)寄存器(WTDAT)

看門狗計數(shù)寄存器(WTCNT)

 

結(jié)合上圖可知,看門狗從一個PCLK頻率到產(chǎn)生一個RESET復(fù)位信號的過程如下:

1,處理器向看門狗提供一個PCLK時鐘信號。其通過一個8位預(yù)分頻器(8-bit Prescaler)使頻率降低

2,8位預(yù)分頻器由控制寄存器WTCON的第8~15位決定。分頻后的頻率就相當(dāng)于PCLK除以(WTCON[15:8]+1).

3,然后再通過一個4相分頻器,分成4種大小的頻率。這4種頻率系數(shù)分別是16,32,64,128.看門狗可以通過寄存器的3,4位決定使用哪種頻率

4,當(dāng)選擇的時鐘頻率到達(dá)計數(shù)器(Down Counter)時,會按照工作頻率將WTCNT減1.當(dāng)達(dá)到0時,就會產(chǎn)生一個中斷信號或者復(fù)位信號

5,如果控制寄存器WTCOON的第二位為1,則發(fā)出一個中斷信號;如果控制寄存器WTCON第0位為1,則輸出一個復(fù)位信號,使系統(tǒng)重新啟動

 

看門狗驅(qū)動涉及兩種設(shè)備模型,分別是平臺設(shè)備和混雜設(shè)備

平臺設(shè)備模型:

從Linux2.6起引入了一套新的驅(qū)動管理和注冊模型,即平臺設(shè)備platform_device和平臺驅(qū)動platform_driver.Linux中大部分的設(shè)備驅(qū)動,都可以使用這套機(jī)制,設(shè)備用platform_device表示,驅(qū)動用platform_driver表示

 

平臺設(shè)備模型與傳統(tǒng)的device和driver模型相比,一個十分明顯的優(yōu)勢在于平臺設(shè)備模型將設(shè)備本身的資源注冊進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理。這樣提高了驅(qū)動和資源管理的獨(dú)立性,并且擁有較好的可移植性和安全性。通過平臺設(shè)備模型開發(fā)底層驅(qū)動的大致流程為下圖:

 

平臺設(shè)備是指處理器上集成的額外功能的附加設(shè)備,如Watch Dog,IIC,IIS,RTC,ADC等設(shè)備。這些額外功能設(shè)備是為了節(jié)約硬件成本、減少產(chǎn)品功耗、縮小產(chǎn)品形狀而集成到處理器內(nèi)部的。需要注意的是,平臺設(shè)備并不是與字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備并列的概念,而是一種平行的概念,其從另一個角度對設(shè)備進(jìn)行了概括。如果從內(nèi)核開發(fā)者的角度來看,平臺設(shè)備的引入,是為了更容易開發(fā)字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備驅(qū)動

平臺設(shè)備結(jié)構(gòu)體(platform_device)

struct platform_device

{

  const  char *name;      //平臺設(shè)備的名字,與驅(qū)動的名字對應(yīng)

  int  id;                        //與驅(qū)動綁定有關(guān),一般為-1

    struct device dev;   //設(shè)備結(jié)構(gòu)體說明platform_device派生于device

  u32  num_resources;  //設(shè)備使用的資源數(shù)量

       struct resource *resource;   //指向資源的數(shù)組,數(shù)量由num_resources指定

};

 

看門狗的平臺設(shè)備為:

struct platform_device s3c_device_wdt = {
    .name          = "s3c2410-wdt",
    .id          = -1,
    .num_resources      = ARRAY_SIZE(s3c_wdt_resource),
    .resource      = s3c_wdt_resource,
};

為了統(tǒng)一管理平臺設(shè)備的資源,在platform_device機(jī)構(gòu)體中定義了平臺設(shè)備所使用的資源。

看門狗的資源如下:

static struct resource s3c_wdt_resource[] = {
    [0] = {  //  I/O資源指向看門狗的寄存器
        .start = S3C24XX_PA_WATCHDOG,          //看門狗I/O內(nèi)存開始位置,被定義為WTCON的地址0x53000000
        .end   = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,                         //1M的地址空間
        .flags = IORESOURCE_MEM,                                                                           //I/O內(nèi)存資源
    },
    [1] = { //IRQ資源
        .start = IRQ_WDT,                                                                                          //看門狗的開始中斷號,被定義為80
        .end   = IRQ_WDT,                                                                                        //看門狗的結(jié)束中斷號
        .flags = IORESOURCE_IRQ,                                                                          //中斷的IRQ資源
    }

};

 

struct resource

{

          resource_size_t  start;            //資源的開始地址,resource_size是32位或者64位的無符號整數(shù)

          resource_size_t  end;             //資源的結(jié)束地址

          const char *name;                  //資源名

          unsigned long flags;               //資源的類型 (IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等)

          struct resource *parent,*sibling,*child;         //用于構(gòu)建資源的樹形結(jié)構(gòu)

};

 

通過platfrom_add_devices()函數(shù)可以將一組設(shè)備添加到系統(tǒng)中,其主要完成以下兩個功能:

1,分配平臺設(shè)備所使用的資源,并將這些資源掛接到資源樹中

2,初始化device設(shè)備,并將設(shè)備注冊到系統(tǒng)中

第一個參數(shù)是平臺設(shè)備數(shù)組的指針,第2個參數(shù)是平臺設(shè)備的數(shù)量

int platform_add_devices(struct platform_device **devs, int num)
{
    int i, ret = 0;

    for (i = 0; i < num; i++) {
        ret = platform_device_register(devs[i]);
        if (ret) {
            while (--i >= 0)
                platform_device_unregister(devs[i]);
            break;
        }
    }

    return ret;
}

 

通過platform_get_resource()函數(shù)可以獲得平臺設(shè)備的resource資源:

第一個參數(shù)dev是平臺設(shè)備的指針,第2個參數(shù)type是資源的類型,這些類型可以是(IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等),第3個參數(shù)num是同種資源的索引。例如一個平臺設(shè)備有3哥IORESOURCE_MEM資源,如果要獲得第2個資源,那么需要使num等于1

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
{
    int i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if (type == resource_type(r) && num-- == 0)
            return r;
    }
    return NULL;
}

平臺設(shè)備驅(qū)動 :

每一個平臺設(shè)備都對應(yīng)一個平臺設(shè)備驅(qū)動,這個驅(qū)動用來對平臺設(shè)備進(jìn)行探測、移除、關(guān)閉和電源管理等操作。

struct platform_driver {
    int (*probe)(struct platform_device *);                        //探測函數(shù)
    int (*remove)(struct platform_device *);                     //移除函數(shù)
    void (*shutdown)(struct platform_device *);                  //關(guān)閉設(shè)備時調(diào)用該函數(shù)
    int (*suspend)(struct platform_device *, pm_message_t state);          //掛起函數(shù)
    int (*suspend_late)(struct platform_device *, pm_message_t state);    //掛起之后調(diào)用的函數(shù)
    int (*resume_early)(struct platform_device *);                                      //恢復(fù)正常狀態(tài)之前調(diào)用的函數(shù)
    int (*resume)(struct platform_device *);                                              //恢復(fù)正常狀態(tài)的函數(shù)
    struct device_driver driver;                                                                 //設(shè)備驅(qū)動核心結(jié)構(gòu)
};

看門狗的平臺設(shè)備驅(qū)動:

static struct platform_driver s3c2410wdt_driver = {
    .probe        = s3c2410wdt_probe,
    .remove        = s3c2410wdt_remove,
    .shutdown    = s3c2410wdt_shutdown,
    .suspend    = s3c2410wdt_suspend,
    .resume        = s3c2410wdt_resume,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "s3c2410-wdt",
    },
};

 

一般來說,在內(nèi)核啟動時,會注冊平臺設(shè)備和平臺設(shè)備驅(qū)動程序。內(nèi)核將在適當(dāng)?shù)臅r候,將平臺設(shè)備和平臺驅(qū)動連接起來。連接的方法,是用系統(tǒng)中的所有平臺設(shè)備和所有已經(jīng)注冊的平臺驅(qū)動進(jìn)行匹配。下面是源代碼:

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev;

    pdev = container_of(dev, struct platform_device, dev);
    return (strcmp(pdev->name, drv->name) == 0);
}

該函數(shù)將由內(nèi)核自己調(diào)用,當(dāng)設(shè)備找到對應(yīng)的驅(qū)動時,會觸發(fā)probe函數(shù)。所以,probe函數(shù)一般是驅(qū)動程序加載成功后的第一個調(diào)用函數(shù),在該函數(shù)中可以申請設(shè)備所需要的資源

driver的綁定是通過driver core自動完成的,完成driver和device的匹配后以后會自動執(zhí)行probe()函數(shù),如果函數(shù)執(zhí)行成功,則driver和device就綁定在一起了,drvier和device匹配的方法有3種: 
>> 當(dāng)一個設(shè)備注冊的時候,他會在總線上尋找匹配的driver,platform device一般在系統(tǒng)啟動很早的時候就注冊了 
>> 當(dāng)一個驅(qū)動注冊[platform_driver_register()]的時候,他會遍歷所有總線上的設(shè)備來尋找匹配,在啟動的過程驅(qū)動的注冊一般比較晚,或者在模塊載入的時候 
>> 當(dāng)一個驅(qū)動注冊[platform_driver_probe()]的時候, 功能上和使用platform_driver_register()是一樣的,唯一的區(qū)別是它不能被以后其他的device probe了,也就是說這個driver只能和一個device綁定。

 

int __init_or_module platform_driver_probe(struct platform_driver *drv,
        int (*probe)(struct platform_device *))
{
    int retval, code;

    /* temporary section violation during probe() */
    drv->probe = probe;
    retval = code = platform_driver_register(drv);

    /* Fixup that section violation, being paranoid about code scanning
     * the list of drivers in order to probe new devices.  Check to see
     * if the probe was successful, and make sure any forced probes of
     * new devices fail.
     */
    spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
    drv->probe = NULL;
    if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
        retval = -ENODEV;
    drv->driver.probe = platform_drv_probe_fail;
    spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);

    if (code != retval)
        platform_driver_unregister(drv);
    return retval;
}

 

static int platform_drv_probe(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    return drv->probe(dev);
}

static int platform_drv_probe_fail(struct device *_dev)
{
    return -ENXIO;
}

static int platform_drv_remove(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    return drv->remove(dev);
}

static void platform_drv_shutdown(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    drv->shutdown(dev);
}

static int platform_drv_suspend(struct device *_dev, pm_message_t state)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    return drv->suspend(dev, state);
}

static int platform_drv_resume(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);

    return drv->resume(dev);
}

 

需要將平臺設(shè)備驅(qū)動注冊到系統(tǒng)中才能使用,內(nèi)核提供了platform_driver_register()函數(shù)實(shí)現(xiàn)這個功能:

int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;                  //平臺總線類型

  //如果定義了probe函數(shù),該函數(shù)將覆蓋driver中定義的函數(shù)(即覆蓋父函數(shù))

    if (drv->probe)
        drv->driver.probe = platform_drv_probe;                                     //默認(rèn)探測函數(shù)
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;                                //默認(rèn)移除函數(shù)
    if (drv->shutdown) 
        drv->driver.shutdown = platform_drv_shutdown;                         //默認(rèn)關(guān)閉函數(shù)
    if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;                             //默認(rèn)掛起函數(shù)
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;                                   //默認(rèn)恢復(fù)函數(shù)
    return driver_register(&drv->driver);                          //將驅(qū)動注冊到系統(tǒng)中
}

在平臺設(shè)備platform_driver和其父結(jié)構(gòu)driver中相同的方法。如果平臺設(shè)備驅(qū)動中定義probe()方法,那么內(nèi)核將會調(diào)用平臺設(shè)備驅(qū)動中的方法;如果平臺設(shè)備驅(qū)動中沒有定義probe()方法,那么將調(diào)用driver中對應(yīng)方法。platform_driver_register()函數(shù)用來完成這種功能,并注冊設(shè)備驅(qū)動到內(nèi)核中。platform_driver注冊到內(nèi)核后,內(nèi)核調(diào)用驅(qū)動的關(guān)系如下圖:

 模塊卸載時需要注銷函數(shù) :

void platform_driver_unregister(struct platform_driver *drv)
{
    driver_unregister(&drv->driver);
}

 

可以使用platform_device_alloc動態(tài)地創(chuàng)建一個設(shè)備:

struct platform_device *platform_device_alloc(const char *name, int id)
{
    struct platform_object *pa;

    pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
    if (pa) {
        strcpy(pa->name, name);
        pa->pdev.name = pa->name;
        pa->pdev.id = id;
        device_initialize(&pa->pdev.dev);
        pa->pdev.dev.release = platform_device_release;
    }

    return pa ? &pa->pdev : NULL;
}

 

一個更好的方法是,通過下面的函數(shù)動態(tài)創(chuàng)建一個設(shè)備,并把這個設(shè)備注冊到系統(tǒng)中:

struct platform_device *platform_device_register_simple(const char *name,
                            int id,
                            struct resource *res,
                            unsigned int num)
{
    struct platform_device *pdev;
    int retval;

    pdev = platform_device_alloc(name, id);
    if (!pdev) {
        retval = -ENOMEM;
        goto error;
    }

    if (num) {
        retval = platform_device_add_resources(pdev, res, num);
        if (retval)
            goto error;
    }

    retval = platform_device_add(pdev);
    if (retval)
        goto error;

    return pdev;

error:
    platform_device_put(pdev);
    return ERR_PTR(retval);
}

獲取資源中的中斷號:

int platform_get_irq(struct platform_device *dev, unsigned int num)
{
    struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

    return r ? r->start : -ENXIO;
}

根據(jù)參數(shù)name所指定的名稱,來獲取資源中的中斷號:

int platform_get_irq_byname(struct platform_device *dev, char *name)
{
    struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
                              name);

    return r ? r->start : -ENXIO;
}

 

Platform device 和 Platform driver實(shí)際上是cpu總線可以直接尋址的設(shè)備和驅(qū)動,他們掛載在一個虛擬的總線platform_bus_type上,是一種bus- specific設(shè)備和驅(qū)動。與其他bus-specific驅(qū)動比如pci是一樣的。他們都是將device和device_driver加了一個 warpper產(chǎn)生,仔細(xì)看看platform_device就可以看到它必然包含一個device dev,而platform_driver也一樣,它必然包含一個device_driver driver。 
所有的設(shè)備通過bus_id 掛在總線上,多個device可以共用一個driver,但是一個device不可以對應(yīng)多個driver。驅(qū)動去注冊時候會根據(jù)設(shè)備名尋找設(shè)備,沒有設(shè) 備會注冊失敗,注冊的過程會通過probe來進(jìn)行相應(yīng)資源的申請,以及硬件的初始化,如果probe執(zhí)行成功,則device和driver的綁定就成功 了。設(shè)備注冊的時候同樣會在總線上尋找相應(yīng)的驅(qū)動,如果找到他也會試圖綁定,綁定的過程同樣是執(zhí)行probe          (參考http://www.cnblogs.com/alfredzzj/archive/2012/07/02/2573699.html)

 

混雜設(shè)備:

混雜設(shè)備并沒有一個明確的定義。由于設(shè)備號比較緊張,所以一些不相關(guān)的設(shè)備可以使用同一主設(shè)備號。主設(shè)備號通常是10.由于這個原因,一些設(shè)備也可以叫做混雜設(shè)備。

struct miscdevice

{

           int minor;                               //次設(shè)備號

           const char *name;               //混雜設(shè)備名字

           const struct file_operations *fops;     //設(shè)備的操作函數(shù),與字符設(shè)備相同

           struct list_head list;                           //連向下一個混雜設(shè)備的鏈表

           struct  device *parent;                  //指向父設(shè)備

           struct   device  *this_device;         指向當(dāng)前設(shè)備結(jié)構(gòu)體

};

 

看門狗的混雜設(shè)備定義:

static struct miscdevice s3c2410wdt_miscdev = {
    .minor        = WATCHDOG_MINOR,            //次設(shè)備號,定義為130
    .name        = "watchdog",                           //混雜設(shè)備名字
    .fops        = &s3c2410wdt_fops,             //混雜設(shè)備操作指針
};

混雜設(shè)備的注冊很簡單,misc_register(),傳遞一個混雜設(shè)備的指針

int misc_register(struct miscdevice *misc);

該函數(shù)內(nèi)部檢測次設(shè)備號是否合法,如果次設(shè)備號被占用,則返回設(shè)備忙狀態(tài)。如果miscdevice的成員minor為255,則嘗試動態(tài)申請一個次設(shè)備號。當(dāng)次設(shè)備號可用時,函數(shù)會將混雜設(shè)備注冊到內(nèi)核設(shè)備模型中

相反,注銷函數(shù):

int  misc_deregister(struct misc_miscdevice *misc);

 

看門狗設(shè)備驅(qū)動程序:

主要變量:

static int nowayout    = WATCHDOG_NOWAYOUT;

    表示絕不允許看門狗關(guān)閉,為1表示不允許關(guān)閉,為0表示允許關(guān)閉,當(dāng)不允許關(guān)閉調(diào)用close()是沒用的.WATCHDOG_NOWAYOUT的取值由配置選項                  

          CONFIG_WATCHDOG_NOWAYOUT決定:

      #ifdef  CONFIG_WATCHDOG_NOWAYOUT

           #define  WATCHDOG_NOWAYOUT      1

           #else

           #define  WATCHDOG_NOWAYOUT       0

           #endif

                                                                       

static int tmr_margin    = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;           //表示默認(rèn)的看門狗喂狗時間我15秒
static int tmr_atboot    = CONFIG_S3C2410_WATCHDOG_ATBOOT;       //表示系統(tǒng)啟動時就使能看門狗。為1表示使能,為0表示關(guān)閉
static int soft_noboot;             //看門狗工作方式,為1表示看門狗作為定時器使用,不發(fā)送復(fù)位信號,為0表示發(fā)送復(fù)位信號
static int debug;                 //是否使用調(diào)試模式來調(diào)試代碼。該模式中會打印調(diào)試信息

 

另一個重要的枚舉值close_state來標(biāo)識看門狗是否允許關(guān)閉

typedef enum close_state {
    CLOSE_STATE_NOT,                          //不允許關(guān)閉看門狗
    CLOSE_STATE_ALLOW = 0x4021        //允許關(guān)閉看門狗
} close_state_t;

 

看門狗的加載和卸載函數(shù):

static char banner[] __initdata =
    KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";

static int __init watchdog_init(void)
{
    printk(banner);
    return platform_driver_register(&s3c2410wdt_driver);
}

static void __exit watchdog_exit(void)
{
    platform_driver_unregister(&s3c2410wdt_driver);
}

 

當(dāng)調(diào)用platform_driver_register()函數(shù)注冊驅(qū)動后,會觸發(fā)設(shè)備和驅(qū)動的匹配函數(shù)platform_match().匹配成功,則會調(diào)用平臺設(shè)備驅(qū)動中的probe()函數(shù),看門狗驅(qū)動中對應(yīng)的函數(shù)是s3c2410wdt_probe():代碼:

static int s3c2410wdt_probe(struct platform_device *pdev)
{
    struct resource *res;            //資源指針
    struct device *dev;                  //設(shè)備結(jié)構(gòu)體指針
    unsigned int wtcon;                  //用于暫時存放WTCON寄存器的數(shù)據(jù)
    int started = 0;
    int ret;
    int size;

    DBG("%s: probe=%p\n", __func__, pdev);

    dev = &pdev->dev;        //平臺設(shè)備中取出設(shè)備device
    wdt_dev = &pdev->dev;

    /* get the memory region for the watchdog timer */

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);         //獲得看門狗的內(nèi)存資源
    if (res == NULL) {                          //失敗退出
        dev_err(dev, "no memory resource specified\n");
        return -ENOENT;
    }

    size = (res->end - res->start) + 1;                   //內(nèi)存資源所占的字節(jié)數(shù)
    wdt_mem = request_mem_region(res->start, size, pdev->name);                //申請一塊I/O內(nèi)存,對應(yīng)看門狗的3個寄存器
    if (wdt_mem == NULL) {                                   //申請設(shè)備退出
        dev_err(dev, "failed to get memory region\n");
        ret = -ENOENT;
        goto err_req;
    }

    wdt_base = ioremap(res->start, size);              //將設(shè)備內(nèi)存映射到虛擬地址空間,這樣可以使用函數(shù)訪問
    if (wdt_base == NULL) {                               //映射失敗退出
        dev_err(dev, "failed to ioremap() region\n");
        ret = -EINVAL;
        goto err_req;
    }

    DBG("probe: mapped wdt_base=%p\n", wdt_base);                //輸出映射基地址,調(diào)試時用
 
    wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);                     //獲得看門狗可以申請的中斷號
    if (wdt_irq == NULL) {                       //獲取中斷號失敗,退出
        dev_err(dev, "no irq resource specified\n");
        ret = -ENOENT;
        goto err_map;
    }

    ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);            //申請中斷,并注冊中斷處理函數(shù)s3c2410wdt_irq()
    if (ret != 0) {                               //申請失敗退出
        dev_err(dev, "failed to install irq (%d)\n", ret);
        goto err_map;
    }

    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);
        goto err_irq;
    }

    clk_enable(wdt_clock);               //使能看門狗時鐘

    /* see if we can actually set the requested timer margin, and if
     * not, try the default value */

    if (s3c2410wdt_set_heartbeat(tmr_margin)) {               //設(shè)置看門狗復(fù)位時間tmr_margin,如果時間值不合法,返回非0,重新設(shè)置默認(rèn)復(fù)位時間
        started = s3c2410wdt_set_heartbeat(
                    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, cannot start\n");
    }

    ret = misc_register(&s3c2410wdt_miscdev);                  //注冊混雜設(shè)備
    if (ret) {                   //注冊失敗退出
        dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
            WATCHDOG_MINOR, ret);
        goto err_clk;
    }

    if (tmr_atboot && started == 0) {              //開機(jī)時就立即啟動看門狗定時器
        dev_info(dev, "starting watchdog timer\n");
        s3c2410wdt_start();                     //啟動看門狗
    } else if (!tmr_atboot) {
        /* if we're not enabling the watchdog, then ensure it is
         * disabled if it has been left running from the bootloader
         * or other source */

        s3c2410wdt_stop();            //停止看門狗
    }

    /* 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) ? "" : "dis",                //看門狗是否允許發(fā)生復(fù)位信號
         (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");                //看門狗是否允許發(fā)出中斷信號

    return 0;

 err_clk:                                   //注冊混雜設(shè)備失敗
    clk_disable(wdt_clock);
    clk_put(wdt_clock);

 err_irq:                   //得到時鐘失敗
    free_irq(wdt_irq->start, pdev);

 err_map:              //獲取中斷失敗
    iounmap(wdt_base);

 err_req:          //申請I/O內(nèi)存失敗
    release_resource(wdt_mem);
    kfree(wdt_mem);

    return ret;
}

設(shè)置看門狗復(fù)位時間函數(shù):s3c2410wdt_set_heartbeat()

該函數(shù)的參數(shù)接受看門狗復(fù)位時間,默認(rèn)值是15秒。該函數(shù)主要完成如下幾個功能:

1,使用clk_set_rate()函數(shù)獲得看門狗的時鐘頻率PCLK

2,判斷復(fù)位時間timeout是否超過計數(shù)寄存器WTCNT能表示的最大值,該寄存器的最大值為65536

3,設(shè)置第一個分頻器的分頻系數(shù)

4,設(shè)置數(shù)據(jù)寄存器WTDAT

源代碼:

static int s3c2410wdt_set_heartbeat(int timeout)
{
    unsigned int freq = clk_get_rate(wdt_clock);     //得到看門狗的時鐘頻率PCLK
    unsigned int count;                                             //將填入WTCNT的計數(shù)值
    unsigned int divisor = 1;                                     //要填入WTCON[15:8]的預(yù)分頻系數(shù)
    unsigned long wtcon;                                         //暫存WTCON的值

    if (timeout < 1)                                                   //看門狗的復(fù)位時間不能小于1秒
        return -EINVAL;

    freq /= 128;                                                        //看門狗默認(rèn)使用128的四相分頻
    count = timeout * freq;                                       //計數(shù)值 = 秒  X  頻率(每秒時鐘滴答)

    DBG("%s: count=%d, timeout=%d, freq=%d\n",
        __func__, count, timeout, freq);                                     //打印相關(guān)的信息用于調(diào)試

    /* if the count is bigger than the watchdog register,
       then work out what we need to do (and if) we can
       actually make this value
    */

    if (count >= 0x10000) {           //最終填入的計數(shù)值不能大于WTCNT的范圍,WTCNT是一個16位寄存器,其最大值為0x10000
        for (divisor = 1; divisor <= 0x100; divisor++) {    //從1到256,尋找一個合適的預(yù)分頻系數(shù)
            if ((count / divisor) < 0x10000)
                break;                 //找到則退出
        }

        if ((count / divisor) >= 0x10000) {                                                //經(jīng)過預(yù)分頻和四相分頻的計數(shù)值仍大于0x10000,則復(fù)位時間太長,看門狗不支持

            dev_err(wdt_dev, "timeout %d too big\n", timeout);
            return -EINVAL;
        }
    }

    tmr_margin = timeout;                          //合法的復(fù)位時間

    DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
        __func__, timeout, divisor, count, count/divisor);                           //打印相關(guān)的調(diào)試信息

    count /= divisor;                                          //分頻后最終的計數(shù)值
    wdt_count = count;

    /* update the pre-scaler */
    wtcon = readl(wdt_base + S3C2410_WTCON);                           //讀WTCNT的值
    wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;                       //將WTCNT的高8位清零
    wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);                         //填入預(yù)分頻系數(shù)

    writel(count, wdt_base + S3C2410_WTDAT);                  //將計數(shù)值寫到數(shù)據(jù)寄存器WTDAT中
    writel(wtcon, wdt_base + S3C2410_WTCON);                //設(shè)置控制寄存器WTCON

    return 0;
}

看門狗開始函數(shù)s3c2410wdt_start():

當(dāng)所有的工作完成后,并且允許看門狗隨機(jī)啟動(tmp_atboot = 1),則會調(diào)用s3c2410wdt_start()函數(shù)使看門狗開始工作 :

static void s3c2410wdt_start(void)
{
    unsigned long wtcon;                   //暫存WTCNT

    spin_lock(&wdt_lock);                      //避免不多線程同時訪問臨界資源

    __s3c2410wdt_stop();                  //先停止看門狗便于設(shè)置

    wtcon = readl(wdt_base + S3C2410_WTCON);            //讀取WTCON的值
    wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;            //通過設(shè)置WTCON的第5位允許看門狗工作,并將第3,4位設(shè)置為11,使用四相分頻

    if (soft_noboot) {                                                //看門狗作為定時器使用
        wtcon |= S3C2410_WTCON_INTEN;                       //使能中斷
        wtcon &= ~S3C2410_WTCON_RSTEN;                     //不允許發(fā)送復(fù)位信號
    } else {                                                                //看門狗作為復(fù)位器使用
        wtcon &= ~S3C2410_WTCON_INTEN;          //禁止發(fā)出中斷
        wtcon |= S3C2410_WTCON_RSTEN;            // 允許發(fā)出復(fù)位信號
    }

    DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n", 
        __func__, wdt_count, wtcon);            //打印相關(guān)調(diào)試信息用于調(diào)試


    writel(wdt_count, wdt_base + S3C2410_WTDAT);     //重新寫數(shù)據(jù)寄存器的值
    writel(wdt_count, wdt_base + S3C2410_WTCNT);     //重新寫計數(shù)寄存器的值
    writel(wtcon, wdt_base + S3C2410_WTCON);            //寫控制寄存器的值
    spin_unlock(&wdt_lock);                                            //自旋鎖解鎖
}

看門狗停止函數(shù):s3c2410wdt_stop():

當(dāng)所有的工作準(zhǔn)備完成后,如果不允許看門狗立即啟動(tmp_atboot = 0),則會調(diào)用s3c2410wdt_stop()函數(shù)使看門狗停止工作:

static void s3c2410wdt_stop(void)
{
    spin_lock(&wdt_lock);
    __s3c2410wdt_stop();
    spin_unlock(&wdt_lock);
}

static void __s3c2410wdt_stop(void)
{
    unsigned long wtcon;            //暫存WTCNT的值

    wtcon = readl(wdt_base + S3C2410_WTCON);                        //讀取WTCON值
    wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);     //設(shè)置WTCON,使看門狗不工作,并且不發(fā)出復(fù)位信號
    writel(wtcon, wdt_base + S3C2410_WTCON);                              //寫控制寄存器的值
}

 

看門狗驅(qū)動程序移除函數(shù):s3c2410wdt_remove():

s3c2440看門狗驅(qū)動程序的移除函數(shù)完成與探測函數(shù)相反的功能。包括釋放I/O內(nèi)存資源、釋放IRQ資源、禁止看門狗時鐘源和注銷混雜設(shè)備:

static int s3c2410wdt_remove(struct platform_device *dev)
{
    release_resource(wdt_mem);             //釋放資源resource
    kfree(wdt_mem);                                   //釋放I/O內(nèi)存
    wdt_mem = NULL;

    free_irq(wdt_irq->start, dev);                  //釋放中斷號
    wdt_irq = NULL;

    clk_disable(wdt_clock);                                //禁止時鐘
    clk_put(wdt_clock);                                        //減少時鐘引用計數(shù)
    wdt_clock = NULL;

    iounmap(wdt_base);                                     //關(guān)閉內(nèi)存映射
    misc_deregister(&s3c2410wdt_miscdev);          //注銷混雜設(shè)備
    return 0;
}

 

當(dāng)看門狗關(guān)閉時,內(nèi)核會自動調(diào)用s3c2410wdt_shutdown()函數(shù)先停止看門狗設(shè)備:

static void s3c2410wdt_shutdown(struct platform_device *dev)
{
    s3c2410wdt_stop();
}

當(dāng)需要暫??撮T狗時,可以調(diào)用s3c2410wdt_suspend()函數(shù)。該函數(shù)保存看門狗的寄存器,并設(shè)置看門狗為停止?fàn)顟B(tài)。該函數(shù)一般由電源管理子模塊調(diào)用,用來節(jié)省電源:

#ifdef CONFIG_PM

static unsigned long wtcon_save;
static unsigned long wtdat_save;

static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
{
    /* Save watchdog state, and turn it off. */           //保存看門狗當(dāng)前狀態(tài),就是WTDAT和WTCON。不需要保存WTCNT
    wtcon_save = readl(wdt_base + S3C2410_WTCON);
    wtdat_save = readl(wdt_base + S3C2410_WTDAT);

    /* Note that WTCNT doesn't need to be saved. */
    s3c2410wdt_stop();

    return 0;
}

與掛起相反的函數(shù)是恢復(fù)函數(shù)s3c2410wdt_resume().該函數(shù)恢復(fù)看門狗寄存器的值。如果掛起之前為停止?fàn)顟B(tài),則恢復(fù)后看門狗為停止?fàn)顟B(tài);如果掛起前為啟動狀態(tài),則恢復(fù)后也為啟動:

static int s3c2410wdt_resume(struct platform_device *dev)
{
    /* Restore watchdog state. */

    writel(wtdat_save, wdt_base + S3C2410_WTDAT);
    writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
    writel(wtcon_save, wdt_base + S3C2410_WTCON);

    printk(KERN_INFO PFX "watchdog %sabled\n",
           (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");

    return 0;
}

 

混雜設(shè)備是一種特殊的字符設(shè)備,所以混雜設(shè)備的操作方法和字符設(shè)備的操作方法基本一樣。

看門狗驅(qū)動中,混雜設(shè)備定義:

static struct miscdevice s3c2410wdt_miscdev = {
    .minor        = WATCHDOG_MINOR,
    .name        = "watchdog",
    .fops        = &s3c2410wdt_fops,
};

static const struct file_operations s3c2410wdt_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .write        = s3c2410wdt_write,
    .unlocked_ioctl    = s3c2410wdt_ioctl,
    .open        = s3c2410wdt_open,
    .release    = s3c2410wdt_release,
};

當(dāng)用戶程序調(diào)用open()函數(shù)時,內(nèi)核會最終調(diào)用s3c2410wdt_open()函數(shù):

1,調(diào)用test_and_set_bit()函數(shù)測試open_lock的第0位。如果open_lock的第0位為0,則表示test_and_set_bit()函數(shù)返回0,表示設(shè)備沒有被另外的程序打開。如果為1,則表示設(shè)備已經(jīng)被打開,返回忙EBUSY狀態(tài)

2,nowayout不為0,表示看門狗絕不允許關(guān)閉,則增加看門狗模塊引用計數(shù)

3,將是否運(yùn)行關(guān)閉變量allow_close()設(shè)為CLOSE_STATE_NOT,表示不允許關(guān)閉設(shè)備

4,使用s3c2410wdt_start()函數(shù)打開設(shè)備

5,使用nonseekable_open()函數(shù)設(shè)置設(shè)備文件file不允許seek操作,即是不允許對設(shè)備進(jìn)行定位

static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
    if (test_and_set_bit(0, &open_lock))            //只允許打開一次
        return -EBUSY;

    if (nowayout)                                    不允許關(guān)閉設(shè)備
        __module_get(THIS_MODULE);      增加引用計數(shù)

    allow_close = CLOSE_STATE_NOT;              //設(shè)為不允許關(guān)閉

    /* start the timer */
    s3c2410wdt_start();             開始運(yùn)行看門狗設(shè)備
    return nonseekable_open(inode, file);                      不允許調(diào)用seek()
}

 

為了使看門狗設(shè)備在調(diào)用close()函數(shù)關(guān)閉后,能夠使用open()方法重新打開,驅(qū)動程序需要定義s3c2410wdt_release()函數(shù)。應(yīng)該在s3c2410wdt_release()函數(shù)中清除open_lock的第0位,是設(shè)備能夠被open()函數(shù)打開。如果看門狗允許關(guān)閉,則應(yīng)該調(diào)用s3c2410wdt_stop()函數(shù)關(guān)閉看門狗。如果不允許關(guān)閉設(shè)備,則調(diào)用s3c2410wdt_keepalive()函數(shù),使看門狗為活動狀態(tài):

static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
    /*
     *    Shut off the timer.
     *     Lock it in if it's a module and we set nowayout
     */

    if (allow_close == CLOSE_STATE_ALLOW)               //看門狗為允許狀態(tài)
        s3c2410wdt_stop();
    else {
        dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
        s3c2410wdt_keepalive();
    }
    allow_close = CLOSE_STATE_NOT;
    clear_bit(0, &open_lock);                                  //將open_lock的第0位設(shè)為0,是原子操作
    return 0;
}
static void s3c2410wdt_keepalive(void)         //相當(dāng)于一個喂狗功能
{
    spin_lock(&wdt_lock);
    writel(wdt_count, wdt_base + S3C2410_WTCNT);             //重寫計數(shù)寄存器WTCNT
    spin_unlock(&wdt_lock);
}

混雜設(shè)備s3c2410wdt_miscdev的file_operations中沒有實(shí)現(xiàn)read()函數(shù),因?yàn)楹苌傩枰獜目撮T狗的寄存器中獲取數(shù)據(jù),但是實(shí)現(xiàn)了寫函數(shù)s3c2410wdt_write()。該函數(shù)主要用來設(shè)置allow_close變量為允許關(guān)閉狀態(tài)。如果想看門狗設(shè)備中寫入V,那么就允許關(guān)閉設(shè)備:

static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
                size_t len, loff_t *ppos)
{
    /*
     *    Refresh the timer.
     */
    if (len) {                                                                                  //有數(shù)據(jù)寫入len不為0
        if (!nowayout) {                                                                   //允許關(guān)閉
            size_t i;

            /* In case it was set long ago */
            allow_close = CLOSE_STATE_NOT;                                         //允許關(guān)閉狀態(tài)

            for (i = 0; i != len; i++) {
                char c;

                if (get_user(c, data + i))
                    return -EFAULT;
                if (c == 'V')
                    allow_close = CLOSE_STATE_ALLOW;
            }
        }
        s3c2410wdt_keepalive();
    }
    return len;
}

s3c2410wdt_ioctl()函數(shù)接受一些系統(tǒng)命令,用來設(shè)置看門狗內(nèi)部狀態(tài):

static long s3c2410wdt_ioctl(struct file *file,    unsigned int cmd,
                            unsigned long arg)
{
    void __user *argp = (void __user *)arg;
    int __user *p = argp;
    int new_margin;

    switch (cmd) {
    case WDIOC_GETSUPPORT:                                                  //獲得看門狗設(shè)備信息,這些信息包含在一個watchdog_info結(jié)構(gòu)體中
        return copy_to_user(argp, &s3c2410_wdt_ident,
            sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
    case WDIOC_GETSTATUS:                                            //和下面的一個,這兩個表示獲得看門狗狀態(tài),一般將0返回給用戶
    case WDIOC_GETBOOTSTATUS:
        return put_user(0, p);
    case WDIOC_KEEPALIVE:                     //對看門狗進(jìn)行喂狗操作
        s3c2410wdt_keepalive();
        return 0;
    case WDIOC_SETTIMEOUT:                       //用來設(shè)置看門狗的新超時時間,并返回舊超時時間。使用get_user()函數(shù)從用戶空間獲得超時時間,并使用s3c2410wdt_set_heartbeat()函數(shù)設(shè)置新的超時時間。通過put_user()函數(shù)返回舊的超時時間
        if (get_user(new_margin, p))
            return -EFAULT;
        if (s3c2410wdt_set_heartbeat(new_margin))
            return -EINVAL;
        s3c2410wdt_keepalive();
        return put_user(tmr_margin, p);
    case WDIOC_GETTIMEOUT:                        //用來獲取當(dāng)前的超時時間
        return put_user(tmr_margin, p);
    default:
        return -ENOTTY;
    }
}

 

看門狗的內(nèi)部存儲單元為一組寄存器,這些寄存器是WTCON,WTDAT,WTCNT。這些寄存器不需要像文件一樣對位置進(jìn)行尋址,所以不需要對llseek()函數(shù)進(jìn)行具體實(shí)現(xiàn)。ESPIPE表示該設(shè)備不允許尋址:

loff_t    no_seek(struct  file  * file, loff_t  offset,  int  origin)

{

         return -ESPIPE;

}

 

當(dāng)看門狗設(shè)備作為定時器使用時,發(fā)出中斷信號,而不是復(fù)位信號。該中斷在探測函數(shù)s3c2410wdt_probe()中通過調(diào)用request_irq()函數(shù)向系統(tǒng)做了申請。中斷處理函數(shù)的只要功能是喂狗操作,使看門狗重新開始計數(shù):

static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
{
    dev_info(wdt_dev, "watchdog timer expired (irq)\n");

    s3c2410wdt_keepalive();
    return IRQ_HANDLED;
}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多