S3C2440驱动篇—看门狗驱动分析


Linux-2.6.32.2内核自带S3C2440看门狗驱动,只需要配置一下就可以使用。驱动源代码位于drivers/watchdog/s3c2410_wdt.c,由于驱动使用了平台设备,有关平台设备学习参考这一篇文章。

驱动实现:

  1. #include <linux/module.h>   
  2. #include <linux/moduleparam.h>   
  3. #include <linux/types.h>   
  4. #include <linux/timer.h>   
  5. #include <linux/miscdevice.h>   
  6. #include <linux/watchdog.h>   
  7. #include <linux/fs.h>   
  8. #include <linux/init.h>   
  9. #include <linux/platform_device.h>   
  10. #include <linux/interrupt.h>   
  11. #include <linux/clk.h>   
  12. #include <linux/uaccess.h>   
  13. #include <linux/io.h>   
  14.   
  15. #include <mach/map.h>   
  16.   
  17. #undef S3C_VA_WATCHDOG   
  18. #define S3C_VA_WATCHDOG (0)   
  19.   
  20. #include <plat/regs-watchdog.h>   
  21.   
  22. #define PFX "s3c2410-wdt: "   
  23.   
  24. #define CONFIG_S3C2410_WATCHDOG_ATBOOT      (0)   
  25. #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME    (15)   
  26.   
  27. static int nowayout = WATCHDOG_NOWAYOUT;  
  28. static int tmr_margin   = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;  
  29. static int tmr_atboot   = CONFIG_S3C2410_WATCHDOG_ATBOOT;  
  30. static int soft_noboot;  
  31. static int debug;  
  32.   
  33. module_param(tmr_margin,  int, 0);  
  34. module_param(tmr_atboot,  int, 0);  
  35. module_param(nowayout,    int, 0);  
  36. module_param(soft_noboot, int, 0);  
  37. module_param(debug,   int, 0);  
  38.   
  39. static unsigned long open_lock;  
  40. static struct device    *wdt_dev;   /* platform device attached to */  
  41. static struct resource  *wdt_mem;  
  42. static struct resource  *wdt_irq;  
  43. static struct clk   *wdt_clock;  
  44. static void __iomem *wdt_base;  
  45. static unsigned int  wdt_count;  
  46. static char      expect_close;  
  47. static DEFINE_SPINLOCK(wdt_lock);  
  48.   
  49. /* watchdog control routines */  
  50.   
  51. #define DBG(msg...) do { \   
  52.     if (debug) \  
  53.         printk(KERN_INFO msg); \  
  54.     } while (0)  
  55.   
  56. /* functions */  
  57.   
  58. static void s3c2410wdt_keepalive(void//喂狗   
  59. {  
  60.     spin_lock(&wdt_lock);  
  61.     writel(wdt_count, wdt_base + S3C2410_WTCNT);  
  62.     spin_unlock(&wdt_lock);  
  63. }  
  64.   
  65. static void __s3c2410wdt_stop(void)  
  66. {  
  67.     unsigned long wtcon;  
  68.   
  69.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  70.     wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);  
  71.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  72. }  
  73.   
  74. static void s3c2410wdt_stop(void)   //停止看门狗   
  75. {  
  76.     spin_lock(&wdt_lock);  
  77.     __s3c2410wdt_stop();  
  78.     spin_unlock(&wdt_lock);  
  79. }  
  80.   
  81. static void s3c2410wdt_start(void)  //启动看门狗   
  82. {  
  83.     unsigned long wtcon;  
  84.   
  85.     spin_lock(&wdt_lock);  
  86.   
  87.     __s3c2410wdt_stop();  
  88.   
  89.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  90.     wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;  
  91.   
  92.     if (soft_noboot) {  
  93.         wtcon |= S3C2410_WTCON_INTEN;  
  94.         wtcon &= ~S3C2410_WTCON_RSTEN;  
  95.     } else {  
  96.         wtcon &= ~S3C2410_WTCON_INTEN;  
  97.         wtcon |= S3C2410_WTCON_RSTEN;  
  98.     }  
  99.   
  100.     DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",  
  101.         __func__, wdt_count, wtcon);  
  102.   
  103.     writel(wdt_count, wdt_base + S3C2410_WTDAT);  
  104.     writel(wdt_count, wdt_base + S3C2410_WTCNT);  
  105.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  106.     spin_unlock(&wdt_lock);  
  107. }  
  108.   
  109. static int s3c2410wdt_set_heartbeat(int timeout)   //根据timeout时间设置看门狗定时器初始值   
  110. {  
  111.     unsigned int freq = clk_get_rate(wdt_clock);  
  112.     unsigned int count;  
  113.     unsigned int divisor = 1;  
  114.     unsigned long wtcon;  
  115.   
  116.     if (timeout < 1)  
  117.         return -EINVAL;  
  118.   
  119.     freq /= 128;  
  120.     count = timeout * freq;  
  121.   
  122.     DBG("%s: count=%d, timeout=%d, freq=%d\n",  
  123.         __func__, count, timeout, freq);  
  124.   
  125.     /* if the count is bigger than the watchdog register, 
  126.        then work out what we need to do (and if) we can 
  127.        actually make this value 
  128.     */  
  129.   
  130.     if (count >= 0x10000) {  
  131.         for (divisor = 1; divisor <= 0x100; divisor++) {  
  132.             if ((count / divisor) < 0x10000)  
  133.                 break;  
  134.         }  
  135.   
  136.         if ((count / divisor) >= 0x10000) {  
  137.             dev_err(wdt_dev, "timeout %d too big\n", timeout);  
  138.             return -EINVAL;  
  139.         }  
  140.     }  
  141.   
  142.     tmr_margin = timeout;  
  143.   
  144.     DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",  
  145.         __func__, timeout, divisor, count, count/divisor);  
  146.   
  147.     count /= divisor;  
  148.     wdt_count = count;  
  149.   
  150.     /* update the pre-scaler */  
  151.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  152.     wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;  
  153.     wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);  
  154.   
  155.     writel(count, wdt_base + S3C2410_WTDAT);  
  156.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  157.   
  158.     return 0;  
  159. }  
  160.   
  161. /* 
  162.  *  /dev/watchdog handling 
  163.  */  
  164.   
  165. static int s3c2410wdt_open(struct inode *inode, struct file *file)  
  166. {  
  167.     if (test_and_set_bit(0, &open_lock))  
  168.         return -EBUSY;  
  169.   
  170.     if (nowayout)     /*nowayout是内涵配置选项,如果配置则看门狗不能被关闭*/  
  171.         __module_get(THIS_MODULE);/*如果内核配置了CONFIG_WATCHDOG_NOWAYOUT项,则使模块使用计数加1*/  
  172.   
  173.     expect_close = 0;  
  174.   
  175.     /* start the timer */  
  176.     s3c2410wdt_start();  
  177.     return nonseekable_open(inode, file);/*表示返回的这个设备文件是不可以被seek操作的,nonseekable_open定义在fs.h中*/  
  178. }  
  179.   
  180. static int s3c2410wdt_release(struct inode *inode, struct file *file)  
  181. {  
  182.     /* 
  183.      *  Shut off the timer. 
  184.      *  Lock it in if it's a module and we set nowayout 
  185.      */  
  186.   
  187.     if (expect_close == 42)  
  188.         s3c2410wdt_stop();  
  189.     else {  
  190.         dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");  
  191.         s3c2410wdt_keepalive();  
  192.     }  
  193.     expect_close = 0;  
  194.     clear_bit(0, &open_lock);  
  195.     return 0;  
  196. }  
  197.   
  198. static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,  
  199.                 size_t len, loff_t *ppos)  
  200. {  
  201.     /* 
  202.      *  Refresh the timer. 
  203.      */  
  204.     if (len) {  
  205.         if (!nowayout) { /*如果没有配置内核CONFIG_WATCHDOG_NOWAYOUT选项,WATCHDOG_NOWAYOUT宏位于include/linux/watchdog.h*/  
  206.             size_t i;  
  207.   
  208.             /* In case it was set long ago */  
  209.             expect_close = 0;  
  210.   
  211.             for (i = 0; i != len; i++) {  
  212.                 char c;  
  213.   
  214.                 if (get_user(c, data + i))  
  215.                     return -EFAULT;  
  216.                 if (c == 'V')  
  217.                     expect_close = 42;  
  218.             }  
  219.         }  
  220. /*上面的意思是想要看门狗定时器可以被关闭,则内核不要配置CONFIG_WATCHDOG_NOWAYOUT选项,对于下面这里还要“喂狗”一次,我刚开始觉得不需要,因为在看门狗定时器中断里面不断的在“喂狗”。后来想想,这里还必须要“喂狗”一次,因为当上面我们判断到写入的数据是"V"时,看门狗定时器的当前操作状态马上被设置为关闭,再当驱动去调用看门狗设备驱动的关闭接口函数时,看门狗定时器中断被禁止,无法再实现“喂狗”,所以这里要手动“喂狗”一次,否则定时器溢出系统被复位*/  
  221.   
  222.         s3c2410wdt_keepalive();  
  223.     }  
  224.     return len;  
  225. }  
  226.   
  227. #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)   
  228.   
  229. /*用于支持看门狗IO控制中获取看门狗信息的命令WDIOC_GETSUPPORT,下面的宏和看门狗信息结构体定义在include/linux/watchdog.h中*/  
  230. static const struct watchdog_info s3c2410_wdt_ident = {  
  231.     .options          =     OPTIONS,  
  232.     .firmware_version = 0,  
  233.     .identity         = "S3C2410 Watchdog",  
  234. };  
  235.   
  236.   
  237. static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,  
  238.                             unsigned long arg)  
  239. {  
  240.     void __user *argp = (void __user *)arg;  
  241.     int __user *p = argp;  
  242.     int new_margin;  
  243.   
  244.     /*以下对看门狗定时器IO控制的命令定义在watchdog.h中*/  
  245.     switch (cmd) {  
  246.     case WDIOC_GETSUPPORT:/*获取看门狗的支持信息,wdt_ident定义在上面*/  
  247.         return copy_to_user(argp, &s3c2410_wdt_ident,sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;  
  248.     case WDIOC_GETSTATUS:  
  249.     case WDIOC_GETBOOTSTATUS:  
  250.         return put_user(0, p);  
  251.     case WDIOC_KEEPALIVE:  
  252.         s3c2410wdt_keepalive();  
  253.         return 0;  
  254.     case WDIOC_SETTIMEOUT:  
  255.         if (get_user(new_margin, p))  
  256.             return -EFAULT;  
  257.         if (s3c2410wdt_set_heartbeat(new_margin))  
  258.             return -EINVAL;  
  259.         s3c2410wdt_keepalive();  
  260.         return put_user(tmr_margin, p);  
  261.     case WDIOC_GETTIMEOUT:  
  262.         return put_user(tmr_margin, p);  
  263.     default:  
  264.         return -ENOTTY;  
  265.     }  
  266. }  
  267.   
  268. /* kernel interface */  
  269.   
  270. static const struct file_operations s3c2410wdt_fops = {  
  271.     .owner      = THIS_MODULE,  
  272.     .llseek     = no_llseek,  /*定义为不可定位,即屏蔽seek操作,no_llseek定义在fs.h中*/  
  273.     .write      = s3c2410wdt_write,  
  274.     .unlocked_ioctl = s3c2410wdt_ioctl,  
  275.     .open       = s3c2410wdt_open,  
  276.     .release    = s3c2410wdt_release,  
  277. };  
  278.   
  279. static struct miscdevice s3c2410wdt_miscdev = {  
  280.     .minor      = WATCHDOG_MINOR,  //位于include/linux/miscdevic.h中   
  281.     .name       = "watchdog",  
  282.     .fops       = &s3c2410wdt_fops,  
  283. };  
  284.   
  285. /* interrupt handler code */  
  286.   
  287. static irqreturn_t s3c2410wdt_irq(int irqno, void *param)  
  288. {  
  289.     dev_info(wdt_dev, "watchdog timer expired (irq)\n");  
  290.   
  291.     s3c2410wdt_keepalive();  
  292.     return IRQ_HANDLED;  
  293. }  
  294. /* device interface */  
  295.   
  296. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)  
  297. {  
  298.     struct resource *res;  /*定义一个资源,用来保存获取的watchdog的IO资源*/  
  299.     struct device *dev;  
  300.     unsigned int wtcon;  
  301.     int started = 0;  
  302.     int ret;  
  303.     int size;  
  304.   
  305.     DBG("%s: probe=%p\n", __func__, pdev);  
  306.   
  307.     dev = &pdev->dev;  
  308.     wdt_dev = &pdev->dev;  
  309.   
  310.     /* get the memory region for the watchdog timer */  
  311.   
  312.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  313.     if (res == NULL) {  
  314.         /*dev_err定义在device.h中,在platform_device.h中已经引用,所以这里就不需再引用*/  
  315.         dev_err(dev, "no memory resource specified\n");  
  316.         return -ENOENT;  
  317.     }  
  318.   
  319.     size = (res->end - res->start) + 1;  
  320.        /*申请watchdog的IO端口资源所占用的IO内存,request_mem_region定义在ioport.h中*/  
  321.     wdt_mem = request_mem_region(res->start, size, pdev->name);  
  322.     if (wdt_mem == NULL) {  
  323.         dev_err(dev, "failed to get memory region\n");  
  324.         ret = -ENOENT;  
  325.         goto err_req;  
  326.     }  
  327.   
  328.     wdt_base = ioremap(res->start, size);  
  329.     if (wdt_base == NULL) {  
  330.         dev_err(dev, "failed to ioremap() region\n");  
  331.         ret = -EINVAL;  
  332.         goto err_req;  
  333.     }  
  334.   
  335.     DBG("probe: mapped wdt_base=%p\n", wdt_base);  
  336.   
  337.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  338.     if (wdt_irq == NULL) {  
  339.         dev_err(dev, "no irq resource specified\n");  
  340.         ret = -ENOENT;  
  341.         goto err_map;  
  342.     }  
  343.   
  344.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);  
  345.     if (ret != 0) {  
  346.         dev_err(dev, "failed to install irq (%d)\n", ret);  
  347.         goto err_map;  
  348.     }  
  349.     /*定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/  
  350.     wdt_clock = clk_get(&pdev->dev, "watchdog");  
  351.     if (IS_ERR(wdt_clock)) {  
  352.         dev_err(dev, "failed to find watchdog clock source\n");  
  353.         ret = PTR_ERR(wdt_clock);  
  354.         goto err_irq;  
  355.     }  
  356.   
  357.     clk_enable(wdt_clock);  
  358.   
  359.     /* see if we can actually set the requested timer margin, and if 
  360.      * not, try the default value */  
  361.   
  362.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {  
  363.         started = s3c2410wdt_set_heartbeat(  
  364.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  365.   
  366.         if (started == 0)  
  367.             dev_info(dev,  
  368.                "tmr_margin value out of range, default %d used\n",  
  369.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  370.         else  
  371.             dev_info(dev, "default timer value is out of range, "  
  372.                             "cannot start\n");  
  373.     }  
  374.   
  375.     ret = misc_register(&s3c2410wdt_miscdev);  
  376.     if (ret) {  
  377.         dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",  
  378.             WATCHDOG_MINOR, ret);  
  379.         goto err_clk;  
  380.     }  
  381.   
  382.     if (tmr_atboot && started == 0) {  
  383.         dev_info(dev, "starting watchdog timer\n");  
  384.         s3c2410wdt_start();  
  385.     } else if (!tmr_atboot) {  
  386.         /* if we're not enabling the watchdog, then ensure it is 
  387.          * disabled if it has been left running from the bootloader 
  388.          * or other source */  
  389.   
  390.         s3c2410wdt_stop();  
  391.     }  
  392.   
  393.     /* print out a statement of readiness */  
  394.   
  395.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  396.   
  397.     dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",  
  398.          (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",  
  399.          (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",  
  400.          (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");  
  401.   
  402.     return 0;  
  403.   
  404.  err_clk:  
  405.     clk_disable(wdt_clock);  
  406.     clk_put(wdt_clock);  
  407.   
  408.  err_irq:  
  409.     free_irq(wdt_irq->start, pdev);  
  410.   
  411.  err_map:  
  412.     iounmap(wdt_base);  
  413.   
  414.  err_req:  
  415.     release_resource(wdt_mem);  
  416.     kfree(wdt_mem);  
  417.   
  418.     return ret;  
  419. }  
  420.   
  421. static int __devexit s3c2410wdt_remove(struct platform_device *dev)  
  422. {  
  423.     release_resource(wdt_mem);  
  424.     kfree(wdt_mem);  
  425.     wdt_mem = NULL;  
  426.   
  427.     free_irq(wdt_irq->start, dev);  
  428.     wdt_irq = NULL;  
  429.   
  430.     clk_disable(wdt_clock);  
  431.     clk_put(wdt_clock);  
  432.     wdt_clock = NULL;  
  433.   
  434.     iounmap(wdt_base);  
  435.     misc_deregister(&s3c2410wdt_miscdev);  
  436.     return 0;  
  437. }  
  438.   
  439. static void s3c2410wdt_shutdown(struct platform_device *dev)  
  440. {  
  441.     s3c2410wdt_stop();  
  442. }  
  443.   
  444. #ifdef CONFIG_PM   
  445. /*对Watchdog平台设备驱动电源管理的支持。CONFIG_PM这个宏定义在内核中,当配置内核时选上电源管理,则Watchdog平台驱动的设备挂起和恢复功能均有效*/  
  446.   
  447. static unsigned long wtcon_save;  
  448. static unsigned long wtdat_save;  
  449.   
  450. static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)  
  451. {  
  452.     /* Save watchdog state, and turn it off. */  
  453.     wtcon_save = readl(wdt_base + S3C2410_WTCON);  
  454.     wtdat_save = readl(wdt_base + S3C2410_WTDAT);  
  455.   
  456.     /* Note that WTCNT doesn't need to be saved. */  
  457.     s3c2410wdt_stop();  
  458.   
  459.     return 0;  
  460. }  
  461.   
  462. static int s3c2410wdt_resume(struct platform_device *dev)  
  463. {  
  464.     /* Restore watchdog state. */  
  465.   
  466.     writel(wtdat_save, wdt_base + S3C2410_WTDAT);  
  467.     writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */  
  468.     writel(wtcon_save, wdt_base + S3C2410_WTCON);  
  469.   
  470.     printk(KERN_INFO PFX "watchdog %sabled\n",  
  471.            (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");  
  472.   
  473.     return 0;  
  474. }  
  475.   
  476. #else   
  477. #define s3c2410wdt_suspend NULL   
  478. #define s3c2410wdt_resume  NULL   
  479. #endif /* CONFIG_PM */   
  480.   
  481. /*注意:这是使用了一个__devexit_p,还有前面使用了__devinit和__devexit,我们还是先来讲讲这个:在Linux内核中,使用了大量不同的宏来标记具有不同作用的函数和数据结构,这些宏在include/linux/init.h头文件中定义,编译器通过这些宏可以把代码优化放到合适的内存位置,以减少内存占用和提高内核效率。__devinit、__devexit就是这些宏之一,在probe()和remove()函数中应该使用__devinit和__devexit宏。又当remove()函数使用了__devexit宏时,则在驱动结构体中一定要使用__devexit_p宏来引用remove(),所以下面就用__devexit_p来引用rtc_remove*/  
  482.   
  483. static struct platform_driver s3c2410wdt_driver = {  
  484.     .probe      = s3c2410wdt_probe,  
  485.     .remove     = __devexit_p(s3c2410wdt_remove),  
  486.     .shutdown   = s3c2410wdt_shutdown,  
  487.     .suspend    = s3c2410wdt_suspend,  
  488.     .resume     = s3c2410wdt_resume,  
  489.     .driver     = {  
  490.         .owner  = THIS_MODULE,  
  491.         .name   = "s3c2410-wdt",   
  492.               /*注意这里的名称一定要和系统中定义平台设备的名称一致,这样才能把平台设备与该平台设备的驱动关联起来*/  
  493.     },  
  494. };  
  495.   
  496.   
  497. static char banner[] __initdata =  
  498.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";  
  499.   
  500. static int __init watchdog_init(void)  
  501. {  
  502.     printk(banner);  
  503.     return platform_driver_register(&s3c2410wdt_driver);  
  504. }  
  505.   
  506. static void __exit watchdog_exit(void)  
  507. {  
  508.     platform_driver_unregister(&s3c2410wdt_driver);  
  509. }  
  510.   
  511. module_init(watchdog_init);  
  512. module_exit(watchdog_exit);  
  513.   
  514. MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "  
  515.           "Dimitry Andric <dimitry.andric@tomtom.com>");  
  516. MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");  
  517. MODULE_LICENSE("GPL");  
  518. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);  
  519. MODULE_ALIAS("platform:s3c2410-wdt");  
  • 1
  • 2
  • 下一页

相关内容