基于S3C2440的Linux-3.6.6移植——看门狗定时器的应用

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

最后再重新编译根文件系统并烧写到开发板上。此时系统虽然开启了看门狗功能,但由于后台不断在喂狗,所以系统仍然可以正常运行。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值