嵌入式Linux驱动——看门狗(watchdog)驱动

2019-07-12 15:07发布

目标平台:TQ2440 
CPU:s3c2440内核版本:2.6.30

1. 看门狗概述

   看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行"喂狗“,如果不这样做,定时器溢出后则将复位CPU。   因此,看门狗通常用于对处于异常状态的CPU进行复位。   具体的概念请自行百度。

2. S3C2440看门狗

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

3. 看门狗驱动

  看门狗驱动代码位于: linux/drivers/char/watchdog/s3c2410_wdt.c

3.1 模块注册以及probe函数

[cpp] view plain copy
  1. static struct platform_driver s3c2410wdt_driver = {  
  2.     .probe        = s3c2410wdt_probe,  
  3.     .remove        = s3c2410wdt_remove,  
  4.     .shutdown    = s3c2410wdt_shutdown,  
  5.     .suspend    = s3c2410wdt_suspend,  
  6.     .resume        = s3c2410wdt_resume,  
  7.     .driver        = {  
  8.         .owner    = THIS_MODULE,  
  9.         .name    = "s3c2410-wdt",  
  10.     },  
  11. };  
  12.   
  13. static char banner[] __initdata =  
  14.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics ";   
  15.   
  16. static int __init watchdog_init(void){printk(banner);return platform_driver_register(&s3c2410wdt_driver);}  
  17.   
  18. module_init(watchdog_init)  
模块的注册函数很简单,直接调用了 platform的驱动注册函数platform_driver_register。 该函数在注册时会调用驱动的probe方法,也即s3c2410wdt_probe函数。 我们来看下这个函数:[cpp] view plain copy
  1. static int s3c2410wdt_probe(struct platform_device *pdev)  
  2. {  
  3.     struct resource *res;  
  4.     struct device *dev;  
  5.     unsigned int wtcon;  
  6.     int started = 0;  
  7.     int ret;  
  8.     int size;  
  9.   
  10.     DBG("%s: probe=%p ", __func__, pdev);  
  11.   
  12.     dev = &pdev->dev;  
  13.     wdt_dev = &pdev->dev;  
  14.   
  15.     /* get the memory region for the watchdog timer */  
  16.     /*获取平台资源,寄存器地址范围*/  
  17.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  18.     if (res == NULL) {  
  19.         dev_err(dev, "no memory resource specified ");  
  20.         return -ENOENT;  
  21.     }  
  22.   
  23.     /*内存申请*/  
  24.     size = (res->end - res->start) + 1;  
  25.     wdt_mem = request_mem_region(res->start, size, pdev->name);  
  26.     if (wdt_mem == NULL) {  
  27.         dev_err(dev, "failed to get memory region ");  
  28.         ret = -ENOENT;  
  29.         goto err_req;  
  30.     }  
  31.   
  32.     /*内存映射*/  
  33.     wdt_base = ioremap(res->start, size);  
  34.     if (wdt_base == NULL) {  
  35.         dev_err(dev, "failed to ioremap() region ");  
  36.         ret = -EINVAL;  
  37.         goto err_req;  
  38.     }  
  39.   
  40.     DBG("probe: mapped wdt_base=%p ", wdt_base);  
  41.       
  42.     /*获取平台资源,看门狗定时器中断号*/  
  43.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  44.     if (wdt_irq == NULL) {  
  45.         dev_err(dev, "no irq resource specified ");  
  46.         ret = -ENOENT;  
  47.         goto err_map;  
  48.     }  
  49.       
  50.     /*注册看门狗定时器中断*/  
  51.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);  
  52.     if (ret != 0) {  
  53.         dev_err(dev, "failed to install irq (%d) ", ret);  
  54.         goto err_map;  
  55.     }  
  56.     /*获取看门狗模块的时钟*/  
  57.     wdt_clock = clk_get(&pdev->dev, "watchdog");  
  58.     if (IS_ERR(wdt_clock)) {  
  59.         dev_err(dev, "failed to find watchdog clock source ");  
  60.         ret = PTR_ERR(wdt_clock);  
  61.         goto err_irq;  
  62.     }  
  63.   
  64.     /*使能该时钟*/  
  65.     clk_enable(wdt_clock);  
  66.   
  67.     /* see if we can actually set the requested timer margin, and if 
  68.      * not, try the default value */  
  69.   
  70.     /*设置定时器模块的时钟频率*/  
  71.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {  
  72.         started = s3c2410wdt_set_heartbeat(  
  73.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  74.   
  75.         if (started == 0)  
  76.             dev_info(dev,  
  77.                "tmr_margin value out of range, default %d used ",  
  78.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  79.         else  
  80.             dev_info(dev, "default timer value is out of range, cannot start ");  
  81.     }  
  82.   
  83.     /*注册混杂设备,设备名watchdog,次设备号130*/  
  84.     ret = misc_register(&s3c2410wdt_miscdev);  
  85.     if (ret) {  
  86.         dev_err(dev, "cannot register miscdev on minor=%d (%d) ",  
  87.             WATCHDOG_MINOR, ret);  
  88.         goto err_clk;  
  89.     }  
  90.   
  91.     /* 
  92.       *如果需要在看门狗模块加载时启动看门狗则 
  93.       *调用s3c2410wdt_start,否则调用s3c2410wdt_stop 
  94.     */  
  95.       
  96.     if (tmr_atboot && started == 0) {  
  97.         dev_info(dev, "starting watchdog timer ");  
  98.         s3c2410wdt_start();  
  99.     } else if (!tmr_atboot) {  
  100.         /* if we're not enabling the watchdog, then ensure it is 
  101.          * disabled if it has been left running from the bootloader 
  102.          * or other source */