基於S3C2440的Linux-3.6.6移植——看門狗定時器的應用

來源:互聯網
上載者:User

儘管在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 &

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

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.