深入浅出~Linux设备驱动之watchdog设备驱动

  看门狗(watchdog )分硬件看门狗和软件看门狗。硬件看门狗是利用一个定时器 电路,其定时输出连接到电路的复位端,程序在一定时间范围内对定时器清零 (俗称 “喂狗”),如果程序出现故障,不在定时周期内复位看门狗,就使得看门狗定时器溢出产生复位信号 并重启系统。软件看门狗原理上一样,只是将硬件电路上的定时器用处理器的内部定 时器代替。

1 看门狗的三个寄存器

1.1 watchdog原理

  S3C2410内部集成了watchdog,提供3 个寄存器对watchdog 进行操作,这3 个寄存器分别为WTCON (watchdog 控制寄存器)、WTDAT (watchdog 数据寄存器)和WTCNT(watchdog 记数寄存器) S3c2440的看门狗的原理框图如下:

  可以看到,看门狗定时器的频率由PCLK提供,其预分频器最大取值为255+1;另外,通过MUX,可以进一步降低频率。定时器采用递减模式,一旦到0,则可以触发看门狗中断以及RESET复位信号。 看门狗定时器的频率的计算公式如下:

t_watchdog = 1/[PCLK/(Prescaler value + 1)/Division_factor]

1.2 开启S3C2410 的看门狗

void enable watchdog ()
     { 

       rWTCON = WTCON DIV64 | WTCON RSTEN;//64分频、开启复位信号
       rWTDAT = 0x8000;//计数目标 

       rWTCON |= WTCON ENABLE;//开启看门狗
      }

1.3 S3C2410 的看门狗 “喂狗”

void feed dog ()
{
    rWTCNT=0x8000;
}

1.4 看门狗的使用例程

void main () 

     { 

       init system (); 

       ... 

       enable watchdog ();//启动看门狗 

       ... 

       while (1) 

       { 

          ... 

         feed dog (); //喂狗 

      } 

    }

2 watchdog中的数据结

  有一类设备被称为 “平台设备”,通常 SoC 系统中集成的独立的外设单元都被当作平台设备处理

2.1 platform_device 结构体

struct platform device 

     { 

       const char  * name;//设备名 

       u32      id; 

       struct device dev; 

       u32      num resources;//设备所使用各类资源数量 

       struct resource * resource;//资源 

     };

2.2 S3C2410 中的平台设备

struct platform device *s3c24xx uart devs[];
struct platform device s3c device usb; //USB 控制器 

       struct platform device s3c device lcd; //LCD 控制器 

       struct platform device s3c device wdt; //看门狗
                                                       2 

       struct platform device s3c device i2c; //I C 控制器 

       struct platform device s3c device iis; //IIS 

       struct platform device s3c device rtc; //实时钟
        ... 

      /*SMDK2410开发板使用的平台设备*/ 

      static struct platform device *smdk2410 devices[]  initdata = 

      { 

         &s3c device usb, //USB 

         &s3c device lcd, //LCD 

         &s3c device wdt, //看门狗 

         &s3c device i2c, //I C

         &s3c device iis,  //IIS 

      };

2.3 S3C2410 看门狗的platform_device 结构体

struct platform device s3c device wdt = 

      { 

        .name = "s3c2410-wdt",  //设备名 

        .id =  - 1, . 

       num resources = ARRAY SIZE (s3c wdt resource),  //资源数量 

        .resource = s3c wdt resource,  //看门狗所使用资源 

      };

2.4 int platform_add_devices()函数

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; 

     }

2.5 platform_driver 结构体

struct platform driver
      { 

        int (*probe)(struct platform device *);//探测 

        int (*remove)(struct platform device *);//移除 

       void (*shutdown)(struct platform device *);//关闭 

        int (*suspend)(struct platform device *, pm message t state);// 挂起 

        int (*resume)(struct platform device *);//恢复 

        struct device driver driver; 

       };

2.6 S3C2410 看门狗的platform_driver 结构体

static struct platform driver s3c2410wdt driver = 

      { 

       .probe      = s3c2410wdt probe,//S3C2410看门狗探测 

       .remove     = s3c2410wdt remove,// S3C2410看门狗移除 

       .shutdown   = s3c2410wdt shutdown,//S3C2410看门狗关闭 

       .suspend    = s3c2410wdt suspend,//S3C2410看门狗挂起 

        .resume     = s3c2410wdt resume, //S3C2410看门狗恢复 

        .driver     = { 

            .owner  = THIS MODULE, 

           .name   = "s3c2410-wdt",//设备名 

      },
};

2.7 S3C2410 看门狗所用资源

static struct resource s3c wdt resource[] = 

       { 

         [0] = 

        { 

          .start = S3C24XX PA WATCHDOG,     //看门狗I/O 内存开始位置 

         .end = S3C24XX PA WATCHDOG + S3C24XX SZ WATCHDOG - 1, 

              //看门狗I/O 内存结束位置 

          .flags = IORESOURCE MEM,  //I/O 内存资源 

        } , 

        [1] = 

       { 

         .start = IRQ WDT, //看门狗开始IRQ 号 

         .end = IRQ WDT, //看门狗结束IRQ 号 

         .flags = IORESOURCE IRQ,  //IRQ 资源 

        } 

      };

2.8 S3C2410 看门狗驱动的miscdevice 结构体

 static struct miscdevice s3c2410wdt miscdev =
 { 

        .minor      = WATCHDOG MINOR,//次设备号 

        .name       = "watchdog", 

       .fops       = &s3c2410wdt fops,//文件操作结构体 

};

2.9 S3C2410 看门狗驱动的文件操作结构体

static struct file operations s3c2410wdt fops =
{ 

        .owner      = THIS MODULE, 

        .llseek     = no llseek,    //seek 

        .write      = s3c2410wdt write,     //写函数 

        .ioctl      = s3c2410wdt ioctl, //ioctl 函数 

        .open       = s3c2410wdt open,  //打开函数 

        .release    = s3c2410wdt release,//释放函数 

};

3 加载和卸载函数

  驱动模块的加载和卸载函数分别 用 platform_driver_register() 和 platform_driver_ unregister()注册和注销platform_driver

3.1 S3C2410 看门狗驱动

static int     __init watchdog__init (void)
      {
       printk (banner); 

        return   platform driver register(&s3c2410wdt driver);//       注 册platform driver
      } 

      static void     exit watchdog exit (void)
       {
platform driver unregister (&s3c2410wdt driver);//          注  销 platform driver
     }

4 探测和移除函数

4.1 探测函数

static int s3c2410wdt probe (struct platform device *pdev)
      {
        struct resource *res;
        int started = 0;
        int ret;
        int size; 

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

       /* 获得看门狗的内存区域 */ 

       res = platform get resource (pdev, IORESOURCE MEM, 0);
       if (res == NULL)
        { 

        printk(KERN INFO PFX "failed to get memory region resouce\n");
         return  - ENOENT;
        } 

       size = (res->end - res->start) + 1;
       //申请I/O 内存 

       wdt mem = request mem region (res->start, size, pdev->name); 

       if (wdt mem == NULL)
        { 

         printk(KERN INFO PFX "failed to get memory region\n");
         return  - ENOENT;
        } 

       wdt base = ioremap (res->start, size); //设备内存->虚拟地址 

       if (wdt base == 0)
        { 

         printk(KERN INFO PFX "failed to ioremap () region\n");
         return  - EINVAL;
        } 

       DBG ("probe: mapped wdt base=%p\n", wdt base); 

       /* 获得看门狗的中断 */ 

       res = platform get resource (pdev, IORESOURCE IRQ, 0);
       if (res == NULL)
{ 

         printk(KERN INFO PFX "failed to get irq resource\n");
         return  - ENOENT;
        }
       //申请中断 

         ret = request irq (res->start, s3c2410wdt irq, 0, pdev->name,
pdev);
       if (ret != 0)
        { 

         printk(KERN INFO PFX "failed to install irq (%d)\n", ret);
         return ret;
        } 

       wdt clock = clk get (&pdev->dev, "watchdog"); //获得看门狗时钟源 

       if (wdt clock == NULL)
        { 

         printk(KERN INFO PFX "failed to find watchdog clock source\n");
         return  - ENOENT;
        } 

       clk enable (wdt clock); 

       /* 看看是否能设置定时器的超时时间为期望的值,如果不能,使用缺省值 */ 

       if (s3c2410wdt set heartbeat (tmr margin))
        { 

         started = s3c2410wdt set heartbeat ( 

             CONFIG S3C2410 WATCHDOG DEFAULT TIME);
         if (started == 0)
         { 

           printk (KERN INFO PFX "tmr margin value out of range, default
%d 

             used\n",CONFIG S3C2410 WATCHDOG DEFAULT TIME);
         }
         else
         { 

           printk (KERN INFO PFX
             "default timer value is out of range, cannot start\n");
         }
        } 

      //注册miscdevice 

       ret = misc register(&s3c2410wdt miscdev);
       if (ret)
        { 

         printk(KERN ERR PFX "cannot registermiscdev on minor=%d (%d)\n", 

           WATCHDOG MINOR, ret);
         return ret;
        } 

       if (tmr atboot && started == 0)
        { 

         printk(KERN INFO PFX "Starting Watchdog Timer\n"); 

        s3c2410wdt start ();
        } 

       return 0;
     }

4.2 探测函数

static int s3c2410wdt remove (struct platform device *dev) 

      { 

        if (wdt mem != NULL) 

        { 

          release resource (wdt mem); //释放资源 

          kfree (wdt mem);//释放内存 

          wdt mem = NULL; 

       } 

      //释放中断 

   if (wdt irq != NULL) 

       { 

         free irq (wdt irq->start, dev); 

         wdt irq = NULL;
} 

       //禁止时钟源 

       if (wdt clock != NULL) 

       { 

         clk disable (wdt clock); 

         clk put (wdt clock); 

         wdt clock = NULL; 

        }
    misc deregister(&s3c2410wdt miscdev);//注销miscdevice 

       return 0; 

    }

5 挂起和恢复函数

5.1 挂起函数

static   int  s3c2410wdt suspend (struct    platform device   *dev, 

pm message t state)
      {
        /* 保存看门狗状态,停止它 */ 

       wtcon save = readl (wdt base + S3C2410 WTCON); 

       wtdat save = readl (wdt base + S3C2410 WTDAT); 

        s3c2410wdt stop (); 

        return 0;
     }

5.2 恢复函数

static int s3c2410wdt resume (struct platform device *dev)
      {
        /* 恢复看门狗状态 */ 

        writel (wtdat save, wdt base + S3C2410 WTDAT); 

        writel (wtdat save, wdt base + S3C2410 WTCNT); 

        writel (wtcon save, wdt base + S3C2410 WTCON); 

        printk (KERN INFO PFX "watchdog %sabled\n", (wtcon save 

          &S3C2410 WTCON ENABLE)? "en" : "dis"); 

       return 0;
     }

6 打开和释放函数

6.1 打开函数

static int s3c2410wdt open (struct inode *inode, struct file *file)
      { 

        if (down trylock (&open lock))  //获得打开锁
          return  - EBUSY; 

        if (nowayout)
        { 

            module get (THIS MODULE);
        }
       else
        { 

         allow close = CLOSE STATE ALLOW;
        } 

       /* 启动看门狗 */ 

       s3c2410wdt start (); 

       return nonseekable open (inode, file);
     }

6.2 释放函数

static int s3c2410wdt release (struct inode *inode,struct file *file)
      {
        /* 停止看门狗 */ 

        if (allow close == CLOSE STATE ALLOW)
        { 

          s3c2410wdt stop ();
       }
        else
        { 

              printk (KERN CRIT  PFX  "Unexpected  close,  not  stopping
watchdog!\n"); 

         s3c2410wdt keepalive ();
        } 

       allow close = CLOSE STATE NOT; 

       up (&open lock);  //释放打开锁
       return 0;
     }

7 启停watchdog函数和写函数

7.1 启停看门狗函数

/*停止看门狗*/ 

       static int s3c2410wdt stop (void)
       {
        unsigned long wtcon; 

        wtcon = readl(wdt base + S3C2410 WTCON);
        //停止看门狗,禁止复位 

        wtcon &= ~ (S3C2410 WTCON ENABLE | S3C2410 WTCON RSTEN); 

        writel (wtcon, wdt base + S3C2410 WTCON); 

        return 0;
      } 

      /*开启看门狗*/ 

      static int s3c2410wdt start (void)
      {
        unsigned long wtcon; 

        s3c2410wdt stop (); 

        wtcon = readl(wdt base + S3C2410 WTCON);
        //使能看门狗,128 分频 

        wtcon |= S3C2410 WTCON ENABLE | S3C2410 WTCON DIV128; 

        if (soft noboot)
        { 

         wtcon |= S3C2410 WTCON INTEN; //使能中断 

         wtcon &= ~S3C2410 WTCON RSTEN; //禁止复位
        }
        else
        { 

         wtcon &= ~S3C2410 WTCON INTEN; //禁止中断 

         wtcon |= S3C2410 WTCON RSTEN; //使能复位
        } 

        DBG ("%s: wdt count=0x%08x, wtcon=%08lx\n",        FUNCTION    , 

          wdt count, wtcon); 

        writel (wdt count, wdt base + S3C2410 WTDAT); 

        writel (wdt count, wdt base + S3C2410 WTCNT); 

        writel (wtcon, wdt base + S3C2410 WTCON); 

        return 0;
      }

7.2 写函数

static ssize t s3c2410wdt write (struct file *file, const char      user *data, 

        size t len, loff t *ppos)
       {
        /* 刷新看门狗 */
        if (len)
         {
          if (!nowayout)
{ 

            size t i; 

           allow close = CLOSE STATE NOT;
           for (i = 0; i != len; i++)
           {
             char c; 

             if (get user(c, data + i))//用户空间->内核空间
               return  - EFAULT;
             if (c == ‘V ‘)  //如果写入了‘V ‘ ,允许关闭 

               allow close = CLOSE STATE ALLOW;
           }
         } 

         s3c2410wdt keepalive ();
        }
       return len;
     }

  

时间: 2024-09-29 16:48:17

深入浅出~Linux设备驱动之watchdog设备驱动的相关文章

【转】深入浅出:Linux设备驱动之字符设备驱动

深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

Linux设备驱动开发 - 平台设备驱动

Linux2.6的内核中引入了一种新的设备驱动模型-平台(platform)设备驱动,平台设备驱动分为平台设备(platform_device)和平台驱动(platform_driver),平台设备的引入使得Linux设备驱动更加便于移植. 一.平台设备平台设备结构体: 1 struct platform_device { 2 const char * name; /* 设备名 */ 3 int id; 4 struct device dev; /* 设备结构体 */ 5 u32 num_res

Linux下SPI和IIC驱动免在设备树上添加设备信息的编写方法

编写驱动时,一般需要往设备树上添加节点信息,这里提供一种直接在驱动中添加设备信息的方法. i2c的驱动模板如下 #include <linux/module.h> #include <linux/i2c.h> #define SENSOR_BUS_NUM 0 #define SENSOR_SLAVE_ADDRESS 0x3e #define SENSOR_NAME "sensor" struct i2c_client *sensor_client=NULL; s

linux内核学习之总线、驱动、设备、kset、kobject

最近在研究总线的注册.设备与驱动在总线上的注册.驱动如何找到总线上的设备进行匹配.设备又如何找到总线上的设备进行匹配,在linux2.6以后,这些过程都离不开设备驱动模型,所以也与kset.kobjcet有关. kobject就是一个对象,kset就是所有相同对象的集合,linux的设备驱动模型是用C语言实现面向对象.用linux时使用ls命令查看的文件和目录就是对应每一个kobject. 一.设备device.驱动device_driver.总线bus_type.kobject.kset结构如

Linux应用程序访问字符设备驱动详细过程解析

下面先通过一个编写好的内核驱动模块来体验以下字符设备驱动 可以暂时先忽略下面的代码实现! memdev.c #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/uaccess.h> int dev1_registers[5]; int dev2_registers[5]; stru

9、Linux驱动的杂项设备

杂项设备,是字符设备中的特殊,它的主设备号,是 10,不同的杂项设备,通过次设备号进行区分. 1.注册与注销 int misc_register(struct miscdevice * misc) 完成杂项设备的注册, int misc_deregister(struct miscdevice *misc) 可见,设备的注册和注销,都是设置到 struct miscdevice  结构体 2.struct miscdevice  结构体 struct miscdevice { int minor

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备. 应用程序调用的流程框图: 三种设备的定义分别如下, 字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序进行.字符设备是面向流的设备,常见的字符设备如鼠标.键盘.串口.控制台.LED等. 块设备:是指可以从设备的任意位置读取一定长度的数据设备.块设备如硬盘.磁盘.U盘和SD卡等存储设备. 网络设备:网络设备比较特殊,不在是对文件进行操作,而是由专门的网络接口来实现.应用程序不能直接访