• 蜕变成蝶~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
    ", _ _FUNCTION_ _, pdev); 
          
           /* 获得看门狗的内存区域 */ 
          
                         
           res = platform get resource (pdev, IORESOURCE MEM, 0); 
           if (res == NULL) 
            { 
                    
            printk(KERN INFO PFX "failed to get memory region resouce
    "); 
             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
    "); 
             return  - ENOENT; 
            } 
          
              
           wdt base = ioremap (res->start, size); //设备内存->虚拟地址 
                  
           if (wdt base == 0) 
            { 
                       
             printk(KERN INFO PFX "failed to ioremap () region
    "); 
             return  - EINVAL; 
            } 
          
                               
           DBG ("probe: mapped wdt base=%p
    ", wdt base); 
          
           /* 获得看门狗的中断 */ 
          
                       
           res = platform get resource (pdev, IORESOURCE IRQ, 0); 
           if (res == NULL) 
    { 
                      
             printk(KERN INFO PFX "failed to get irq resource
    "); 
             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)
    ", ret); 
             return ret; 
            } 
          
               
           wdt clock = clk get (&pdev->dev, "watchdog"); //获得看门狗时钟源 
             
           if (wdt clock == NULL) 
            { 
                      
             printk(KERN INFO PFX "failed to find watchdog clock source
    "); 
             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
    ",CONFIG S3C2410 WATCHDOG DEFAULT TIME); 
             } 
             else 
             { 
                         
               printk (KERN INFO PFX 
                 "default timer value is out of range, cannot start
    "); 
             } 
            } 
          
          //注册miscdevice 
                        
           ret = misc register(&s3c2410wdt miscdev); 
           if (ret) 
            { 
                          
             printk(KERN ERR PFX "cannot registermiscdev on minor=%d (%d)
    ", 
                         
               WATCHDOG MINOR, ret); 
             return ret; 
            } 
          
                  
           if (tmr atboot && started == 0) 
            { 
                        
             printk(KERN INFO PFX "Starting Watchdog Timer
    "); 
                    
            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
    ", (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!
    "); 
               
             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
    ",        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; 
         } 
    

      版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4501953.html

  • 相关阅读:
    二叉平衡树
    红黑树
    [leetcode] LCP 比赛
    二叉搜索树
    面向对象的二叉树的实现
    二叉树的序列化与反序列化
    [leetcode] 基本计算器
    【pandas】玩转一行拆多行,多行并一行(分分合合你说了算)
    【VBA】数据溢出与解决
    【VBA】criterial 未找到命名参数
  • 原文地址:https://www.cnblogs.com/lihuidashen/p/4501953.html
Copyright © 2020-2023  润新知