mini6410 看门狗源码分析


看门狗代码放在linux/drivers/char/watchdog/s3c2410_wdt.c中分析如下:

  1. /* linux/drivers/char/watchdog/s3c2410_wdt.c 
  2.  * 
  3.  * Copyright (c) 2004 Simtec Electronics 
  4.  *  Ben Dooks <ben@simtec.co.uk> 
  5.  * 
  6.  * S3C2410 Watchdog Timer Support 
  7.  * 
  8.  * Based on, softdog.c by Alan Cox, 
  9.  *     (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> 
  10.  * 
  11.  * This program is free software; you can redistribute it and/or modify 
  12.  * it under the terms of the GNU General Public License as published by 
  13.  * the Free Software Foundation; either version 2 of the License, or 
  14.  * (at your option) any later version. 
  15.  * 
  16.  * This program is distributed in the hope that it will be useful, 
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  19.  * GNU General Public License for more details. 
  20.  * 
  21.  * You should have received a copy of the GNU General Public License 
  22.  * along with this program; if not, write to the Free Software 
  23.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  24. */  
  25.   
  26. #include <linux/module.h>   
  27. #include <linux/moduleparam.h>   
  28. #include <linux/types.h>   
  29. #include <linux/timer.h>   
  30. #include <linux/miscdevice.h>   
  31. #include <linux/watchdog.h>   
  32. #include <linux/fs.h>   
  33. #include <linux/init.h>   
  34. #include <linux/platform_device.h>   
  35. #include <linux/interrupt.h>   
  36. #include <linux/clk.h>   
  37. #include <linux/uaccess.h>   
  38. #include <linux/io.h>   
  39. #include <linux/cpufreq.h>   
  40. #include <linux/slab.h>   
  41.   
  42. #include <mach/map.h>   
  43.   
  44. #undef S3C_VA_WATCHDOG   
  45. #define S3C_VA_WATCHDOG (0)   
  46.   
  47. #include <plat/regs-watchdog.h>   
  48.   
  49. #define PFX "s3c2410-wdt: "   
  50.   
  51. #define CONFIG_S3C2410_WATCHDOG_ATBOOT      (0)   
  52. #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME    (15)   
  53. /* 
  54. *nowayout表示决不允许看门狗关闭,为1表示不允许关闭,为0表示允许关闭; 
  55. *tmr_margin表示默认的看门狗喂狗时间为15s; 
  56. *tmr_atboot表示系统启动时就使能看门狗,为1表示使能,为0表示关闭; 
  57. *soft_noboot表示看门狗工作的方式,看门狗可以作为定时器使用也可作为复位硬件使用, 
  58. *soft_noboot为1表示看门狗作为定时器使用,不发送复位信号; 
  59. *debug表示是否使用调试模式来调试代码,该模式中,会打印调试信息。 
  60. */  
  61. static int nowayout = WATCHDOG_NOWAYOUT;  
  62. static int tmr_margin   = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;  
  63. static int tmr_atboot   = CONFIG_S3C2410_WATCHDOG_ATBOOT;  
  64. static int soft_noboot;  
  65. static int debug;  
  66.   
  67. module_param(tmr_margin,  int, 0);  
  68. module_param(tmr_atboot,  int, 0);  
  69. module_param(nowayout,    int, 0);  
  70. module_param(soft_noboot, int, 0);  
  71. module_param(debug,   int, 0);  
  72.   
  73. MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="  
  74.         __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");  
  75. MODULE_PARM_DESC(tmr_atboot,  
  76.         "Watchdog is started at boot time if set to 1, default="  
  77.             __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));  
  78. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="  
  79.             __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");  
  80. MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "  
  81.             "0 to reboot (default 0)");  
  82. MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");  
  83.   
  84. static unsigned long open_lock;  
  85. static struct device    *wdt_dev;   /* platform device attached to */  
  86. static struct resource  *wdt_mem;  
  87. static struct resource  *wdt_irq;  
  88. static struct clk   *wdt_clock;  
  89. static void __iomem *wdt_base;  
  90. static unsigned int  wdt_count;  
  91. static char      expect_close;  
  92. static DEFINE_SPINLOCK(wdt_lock);  
  93.   
  94. /* watchdog control routines */  
  95.   
  96. #define DBG(msg...) do { \   
  97.     if (debug) \  
  98.         printk(KERN_INFO msg); \  
  99.     } while (0)  
  100.   
  101. /* functions */  
  102.   
  103. static void s3c2410wdt_keepalive(void)  
  104. {  
  105.     spin_lock(&wdt_lock);  
  106.     writel(wdt_count, wdt_base + S3C2410_WTCNT);  
  107.     spin_unlock(&wdt_lock);  
  108. }  
  109.   
  110. static void __s3c2410wdt_stop(void)  
  111. {  
  112.     unsigned long wtcon;  
  113.   
  114.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  115.     wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);  
  116.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  117. }  
  118.   
  119. static void s3c2410wdt_stop(void)  
  120. {  
  121.     spin_lock(&wdt_lock);  
  122.     __s3c2410wdt_stop();  
  123.     spin_unlock(&wdt_lock);  
  124. }  
  125.   
  126. static void s3c2410wdt_start(void)  
  127. {  
  128.     unsigned long wtcon;  
  129.   
  130.     spin_lock(&wdt_lock);  
  131.     /*首先停止看门狗*/  
  132.     __s3c2410wdt_stop();  
  133.     /*读看门狗的控制寄存器*/  
  134.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  135.     /*使能看门狗,设置时钟分频值为128*/  
  136.     wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;  
  137.     /*上边说过看门狗一个作为一个正常的16位内部定时器也可作为复位硬件*/   
  138.     if (soft_noboot) {//如果作为内部定时器    
  139.         wtcon |= S3C2410_WTCON_INTEN;//开中断使能    
  140.         wtcon &= ~S3C2410_WTCON_RSTEN;//关闭复位使能   
  141.     } else {//如果作为复位硬件   
  142.         wtcon &= ~S3C2410_WTCON_INTEN;//关闭中断使能   
  143.         wtcon |= S3C2410_WTCON_RSTEN;//开复位使能   
  144.     }  
  145.   
  146.     DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",  
  147.         __func__, wdt_count, wtcon);  
  148. /*这里为什么要设置WTCNT的值,不是WTDAT中的值自动载入WTCNT吗?看看datasheet就知道了,看门狗定时器最初 
  149. *使能的时候,WTDAT寄存器的值不会自动载入时        间计数器,所以WTCNT寄存器必须在使能它之前设置一个初始值 
  150. */    
  151.     writel(wdt_count, wdt_base + S3C2410_WTDAT);  
  152.     writel(wdt_count, wdt_base + S3C2410_WTCNT);  
  153.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  154.     spin_unlock(&wdt_lock);  
  155. }  
  156.   
  157. static inline int s3c2410wdt_is_running(void)  
  158. {  
  159.     return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;  
  160. }  
  161.   
  162. static int s3c2410wdt_set_heartbeat(int timeout)  
  163. {  
  164.     /*首先获得看门狗的时钟*/  
  165.     unsigned long freq = clk_get_rate(wdt_clock);  
  166.     unsigned int count;  
  167.     unsigned int divisor = 1;  
  168.     unsigned long wtcon;  
  169.   
  170.     if (timeout < 1)  
  171.         return -EINVAL;  
  172.   
  173.     freq /= 128;  
  174.     /*秒数乘以每秒的时间滴答等于计数值*/  
  175.     count = timeout * freq;  
  176.   
  177.     DBG("%s: count=%d, timeout=%d, freq=%lu\n",  
  178.         __func__, count, timeout, freq);  
  179.   
  180.     /* if the count is bigger than the watchdog register, 
  181.        then work out what we need to do (and if) we can 
  182.        actually make this value 
  183.     */  
  184.     /*计数值不能大于WTCNT的最大范围,WTCNT是一个16位的计数器,最大值是0x10000*/  
  185.     if (count >= 0x10000) {  
  186.         for (divisor = 1; divisor <= 0x100; divisor++) {  
  187.             if ((count / divisor) < 0x10000)  
  188.                 break;  
  189.         }  
  190.         /*未找到返回错误*/  
  191.         if ((count / divisor) >= 0x10000) {  
  192.             dev_err(wdt_dev, "timeout %d too big\n", timeout);  
  193.             return -EINVAL;  
  194.         }  
  195.     }  
  196.       
  197.     /*看门狗的喂狗时间*/   
  198.     tmr_margin = timeout;  
  199.   
  200.     DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",  
  201.         __func__, timeout, divisor, count, count/divisor);  
  202.   
  203.     count /= divisor;  
  204.     wdt_count = count;  
  205.   
  206.     /* update the pre-scaler */  
  207.     //读看门狗的控制寄存器   
  208.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  209.     //看门狗控制器高8位清零,也就是预分频部分清零   
  210.     wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;  
  211.     //填入预分频系数   
  212.     wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);  
  213.   
  214.     //填入计数值   
  215.     writel(count, wdt_base + S3C2410_WTDAT);  
  216.     //填入控制寄存器的值    
  217.     writel(wtcon, wdt_base + S3C2410_WTCON);  
  218.   
  219.     return 0;  
  220. }  
  221.   
  222. /* 
  223.  *  /dev/watchdog handling 
  224.  */  
  225.   
  226. static int s3c2410wdt_open(struct inode *inode, struct file *file)  
  227. {  
  228.     /*检查open_lock的第0位,如果open_lock的第0位为0,则表示test_and_set_bit的返回值为0,  
  229.         表示wdt设备没有被其他进程打开,如果为1,表示被其他进程打开,返回EBUSY*/    
  230.     if (test_and_set_bit(0, &open_lock))  
  231.         return -EBUSY;  
  232.   
  233.   
  234.     /*如果决不允许关闭看门狗,增加引用计数*/   
  235.     if (nowayout)  
  236.         __module_get(THIS_MODULE);  
  237.     /*设为不允许关闭*/  
  238.     expect_close = 0;  
  239.   
  240.     /* start the timer */  
  241.     /*开启看门狗*/  
  242.     s3c2410wdt_start();  
  243.     /*这些寄存器不需要像文件一样对位置进行寻址*/  
  244.     return nonseekable_open(inode, file);  
  245. }  
  246.   
  247. static int s3c2410wdt_release(struct inode *inode, struct file *file)  
  248. {  
  249.     /* 
  250.      *  Shut off the timer. 
  251.      *  Lock it in if it's a module and we set nowayout 
  252.      */  
  253.   
  254.     if (expect_close == 42)//看门狗为允许关闭状态   
  255.         s3c2410wdt_stop();//关闭看门狗     
  256.     else {  
  257.         dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");  
  258.         s3c2410wdt_keepalive();//否则喂狗   
  259.     }  
  260.     expect_close = 0; //设为不允许关闭    
  261.     clear_bit(0, &open_lock);//将open_lock的第0位设为0,是原子操作   
  262.     return 0;  
  263. }  
  264.   
  265. static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,  
  266.                 size_t len, loff_t *ppos)  
  267. {  
  268.     /* 
  269.      *  Refresh the timer. 
  270.      */  
  271.     if (len) {//有数据写入len不为0     
  272.         if (!nowayout) {//允许关闭    
  273.             size_t i;  
  274.   
  275.             /* In case it was set long ago */  
  276.             expect_close = 0; //关闭允许关闭状态    
  277.   
  278.             for (i = 0; i != len; i++) {  
  279.                 char c;  
  280.   
  281.                 if (get_user(c, data + i))  
  282.                     return -EFAULT;  
  283.                 if (c == 'V')//如果向设备写入'V',就允许关闭设   
  284.                     expect_close = 42;  
  285.             }  
  286.         }  
  287.         s3c2410wdt_keepalive();  
  288.     }  
  289.     return len;  
  290. }  
  291.   
  292. #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)   
  293.   
  294. static const struct watchdog_info s3c2410_wdt_ident = {  
  295.     .options          =     OPTIONS,  
  296.     .firmware_version = 0,  
  297.     .identity         = "S3C2410 Watchdog",  
  298. };  
  299.   
  300.   
  301. static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,  
  302.                             unsigned long arg)  
  303. {  
  304.     void __user *argp = (void __user *)arg;  
  305.     int __user *p = argp;  
  306.     int new_margin;  
  307.   
  308.     switch (cmd) {  
  309.     case WDIOC_GETSUPPORT://获得看门狗设备的信息    
  310.         return copy_to_user(argp, &s3c2410_wdt_ident,  
  311.             sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;  
  312.     case WDIOC_GETSTATUS:  
  313.     case WDIOC_GETBOOTSTATUS:  
  314.         return put_user(0, p);  
  315.     case WDIOC_KEEPALIVE://对看门狗进行喂狗   
  316.         s3c2410wdt_keepalive();  
  317.         return 0;  
  318.     case WDIOC_SETTIMEOUT://设置看门狗的超时时间   
  319.         if (get_user(new_margin, p))  
  320.             return -EFAULT;  
  321.         if (s3c2410wdt_set_heartbeat(new_margin))  
  322.             return -EINVAL;  
  323.         s3c2410wdt_keepalive();  
  324.         return put_user(tmr_margin, p);  
  325.     case WDIOC_GETTIMEOUT:  
  326.         return put_user(tmr_margin, p);  
  327.     default:  
  328.         return -ENOTTY;  
  329.     }  
  330. }  
  331.   
  332. /* kernel interface */  
  333. /* 
  334. *看门狗驱动作为字符驱动的file_operations结构 
  335. */  
  336. static const struct file_operations s3c2410wdt_fops = {  
  337.     .owner      = THIS_MODULE,  
  338.     .llseek     = no_llseek,  
  339.     .write      = s3c2410wdt_write,  
  340.     .unlocked_ioctl = s3c2410wdt_ioctl,  
  341.     .open       = s3c2410wdt_open,  
  342.     .release    = s3c2410wdt_release,  
  343. };  
  344.   
  345. static struct miscdevice s3c2410wdt_miscdev = {  
  346.     .minor      = WATCHDOG_MINOR,  
  347.     .name       = "watchdog",  
  348.     .fops       = &s3c2410wdt_fops,  
  349. };  
  350.   
  351. /* interrupt handler code */  
  352. /* 
  353. *如果选择了看门狗作为内部定时器,则当计数值为0时调用中断处理函数,中断处理函数的主要功能就是喂狗 
  354. */  
  355. static irqreturn_t s3c2410wdt_irq(int irqno, void *param)  
  356. {  
  357.     dev_info(wdt_dev, "watchdog timer expired (irq)\n");  
  358.     /*中断处理中调用喂狗*/  
  359.     s3c2410wdt_keepalive();  
  360.     return IRQ_HANDLED;  
  361. }  
  362.   
  363.   
  364. #ifdef CONFIG_CPU_FREQ   
  365.   
  366. static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,  
  367.                       unsigned long val, void *data)  
  368. {  
  369.     int ret;  
  370.   
  371.     if (!s3c2410wdt_is_running())  
  372.         goto done;  
  373.   
  374.     if (val == CPUFREQ_PRECHANGE) {  
  375.         /* To ensure that over the change we don't cause the 
  376.          * watchdog to trigger, we perform an keep-alive if 
  377.          * the watchdog is running. 
  378.          */  
  379.   
  380.         s3c2410wdt_keepalive();  
  381.     } else if (val == CPUFREQ_POSTCHANGE) {  
  382.         s3c2410wdt_stop();  
  383.   
  384.         ret = s3c2410wdt_set_heartbeat(tmr_margin);  
  385.   
  386.         if (ret >= 0)  
  387.             s3c2410wdt_start();  
  388.         else  
  389.             goto err;  
  390.     }  
  391.   
  392. done:  
  393.     return 0;  
  394.   
  395.  err:  
  396.     dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin);  
  397.     return ret;  
  398. }  
  399.   
  400. static struct notifier_block s3c2410wdt_cpufreq_transition_nb = {  
  401.     .notifier_call  = s3c2410wdt_cpufreq_transition,  
  402. };  
  403.   
  404. static inline int s3c2410wdt_cpufreq_register(void)  
  405. {  
  406.     return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb,  
  407.                      CPUFREQ_TRANSITION_NOTIFIER);  
  408. }  
  409.   
  410. static inline void s3c2410wdt_cpufreq_deregister(void)  
  411. {  
  412.     cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb,  
  413.                     CPUFREQ_TRANSITION_NOTIFIER);  
  414. }  
  415.   
  416. #else   
  417. static inline int s3c2410wdt_cpufreq_register(void)  
  418. {  
  419.     return 0;  
  420. }  
  421.   
  422. static inline void s3c2410wdt_cpufreq_deregister(void)  
  423. {  
  424. }  
  425. #endif   
  426.   
  427.   
  428.   
  429. /* device interface */  
  430.   
  431. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)  
  432. {  
  433.     struct resource *res;  
  434.     struct device *dev;  
  435.     unsigned int wtcon;  
  436.     int started = 0;  
  437.     int ret;  
  438.     int size;  
  439.   
  440.     DBG("%s: probe=%p\n", __func__, pdev);  
  441.     /*平台设备中的设备结构体device*/  
  442.     dev = &pdev->dev;  
  443.     wdt_dev = &pdev->dev;  
  444.   
  445.     /* get the memory region for the watchdog timer */  
  446.      /*获得看门狗的内存资源*/  
  447.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  448.     /*获取失败则推出*/  
  449.     if (res == NULL) {  
  450.         dev_err(dev, "no memory resource specified\n");  
  451.         return -ENOENT;  
  452.     }  
  453.     /*内存资源所占的字节数*/  
  454.     size = resource_size(res);  
  455.     /*申请一块IO内存,对应看门狗的3个寄存器*/  
  456.     wdt_mem = request_mem_region(res->start, size, pdev->name);  
  457.     if (wdt_mem == NULL) {  
  458.         dev_err(dev, "failed to get memory region\n");  
  459.         return -EBUSY;  
  460.     }  
  461.     /*获得虚拟地址*/   
  462.     wdt_base = ioremap(res->start, size);  
  463.     if (wdt_base == NULL) {  
  464.         dev_err(dev, "failed to ioremap() region\n");  
  465.         ret = -EINVAL;  
  466.         goto err_req;  
  467.     }  
  468.   
  469.     DBG("probe: mapped wdt_base=%p\n", wdt_base);  
  470.     /*申请中断*/  
  471.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  472.     if (wdt_irq == NULL) {  
  473.         dev_err(dev, "no irq resource specified\n");  
  474.         ret = -ENOENT;  
  475.         goto err_map;  
  476.     }  
  477.       
  478.     /*并注册中断处理函数*/  
  479.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);  
  480.     if (ret != 0) {  
  481.         dev_err(dev, "failed to install irq (%d)\n", ret);  
  482.         goto err_map;  
  483.     }  
  484.     /*获得看门狗时钟*/   
  485.     wdt_clock = clk_get(&pdev->dev, "watchdog");  
  486.     if (IS_ERR(wdt_clock)) {  
  487.         dev_err(dev, "failed to find watchdog clock source\n");  
  488.         ret = PTR_ERR(wdt_clock);  
  489.         goto err_irq;  
  490.     }  
  491.     /*时钟使能*/   
  492.     clk_enable(wdt_clock);  
  493.   
  494.     if (s3c2410wdt_cpufreq_register() < 0) {  
  495.         printk(KERN_ERR PFX "failed to register cpufreq\n");  
  496.         goto err_clk;  
  497.     }  
  498.   
  499.     /* see if we can actually set the requested timer margin, and if 
  500.      * not, try the default value */  
  501.     /*设置看门狗复位时间,如果成功返回0*/  
  502.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {  
  503.         /*如果不成功,看门狗的复位时间设置成默认值*/  
  504.         started = s3c2410wdt_set_heartbeat(  
  505.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  506.   
  507.         if (started == 0)  
  508.             dev_info(dev,  
  509.                "tmr_margin value out of range, default %d used\n",  
  510.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);  
  511.         else  
  512.             dev_info(dev, "default timer value is out of range, "  
  513.                             "cannot start\n");  
  514.     }  
  515.     /*注册混杂设备*/  
  516.     ret = misc_register(&s3c2410wdt_miscdev);  
  517.     if (ret) {  
  518.         dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",  
  519.             WATCHDOG_MINOR, ret);  
  520.         goto err_cpufreq;  
  521.     }  
  522.     /*如果开机的时候就启动看门狗*/  
  523.     if (tmr_atboot && started == 0) {  
  524.         dev_info(dev, "starting watchdog timer\n");  
  525.         s3c2410wdt_start();//启动看门狗    
  526.     } else if (!tmr_atboot) {  
  527.         /* if we're not enabling the watchdog, then ensure it is 
  528.          * disabled if it has been left running from the bootloader 
  529.          * or other source */  
  530.   
  531.         s3c2410wdt_stop();//关闭看门狗   
  532.     }  
  533.   
  534.     /* print out a statement of readiness */  
  535.     /*读出看门狗控制寄存器的值*/  
  536.     wtcon = readl(wdt_base + S3C2410_WTCON);  
  537.     /*打印看门狗是否使能,是否允许发出复位信号,是否允许发出中断信号*/   
  538.     dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",  
  539.          (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",  
  540.          (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",  
  541.          (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");  
  542.   
  543.     return 0;  
  544.   
  545.  err_cpufreq:  
  546.     s3c2410wdt_cpufreq_deregister();  
  547.   
  548.  err_clk:  
  549.     clk_disable(wdt_clock);  
  550.     clk_put(wdt_clock);  
  551.   
  552.  err_irq:  
  553.     free_irq(wdt_irq->start, pdev);  
  554.   
  555.  err_map:  
  556.     iounmap(wdt_base);  
  557.   
  558.  err_req:  
  559.     release_resource(wdt_mem);  
  560.     kfree(wdt_mem);  
  561.   
  562.     return ret;  
  563. }  
  564.   
  565. static int __devexit s3c2410wdt_remove(struct platform_device *dev)  
  566. {  
  567.     misc_deregister(&s3c2410wdt_miscdev);//注销混杂设备   
  568.   
  569.     s3c2410wdt_cpufreq_deregister();  
  570.   
  571.     clk_disable(wdt_clock);//关闭时钟   
  572.     clk_put(wdt_clock);//减少时钟引用计数   
  573.     wdt_clock = NULL;  
  574.   
  575.     free_irq(wdt_irq->start, dev);//释放中断号   
  576.     wdt_irq = NULL;  
  577.   
  578.     iounmap(wdt_base);//关闭内存映射   
  579.   
  580.     release_resource(wdt_mem);//释放平台资源   
  581.     kfree(wdt_mem);//释放I/O内存   
  582.     wdt_mem = NULL;  
  583.     return 0;  
  584. }  
  585.   
  586. static void s3c2410wdt_shutdown(struct platform_device *dev)  
  587. {  
  588.     s3c2410wdt_stop();  
  589. }  
  590.   
  591. #ifdef CONFIG_PM   
  592.   
  593. static unsigned long wtcon_save;  
  594. static unsigned long wtdat_save;  
  595. /* 
  596. *平台驱动中的电源管理部分 
  597. */  
  598. static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)  
  599. {  
  600.     /* Save watchdog state, and turn it off. */  
  601.     /*只要保存S3C2410_WTDAT就可以了,不需要保存S3C2410_WTCNT, 
  602.     *S3C2410_WTCNT的值会在系统还原的时候直接赋为S3C2410_WTDAT中的值 
  603.     */   
  604.     wtcon_save = readl(wdt_base + S3C2410_WTCON);  
  605.     wtdat_save = readl(wdt_base + S3C2410_WTDAT);  
  606.   
  607.     /* Note that WTCNT doesn't need to be saved. */  
  608.     s3c2410wdt_stop();  
  609.   
  610.     return 0;  
  611. }  
  612.   
  613. static int s3c2410wdt_resume(struct platform_device *dev)  
  614. {  
  615.     /* Restore watchdog state. */  
  616.   
  617.     writel(wtdat_save, wdt_base + S3C2410_WTDAT);  
  618.     writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */  
  619.     writel(wtcon_save, wdt_base + S3C2410_WTCON);  
  620.   
  621.     printk(KERN_INFO PFX "watchdog %sabled\n",  
  622.            (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");  
  623.   
  624.     return 0;  
  625. }  
  626.   
  627. #else   
  628. #define s3c2410wdt_suspend NULL   
  629. #define s3c2410wdt_resume  NULL   
  630. #endif /* CONFIG_PM */   
  631.   
  632. /* 
  633. *S3C2410的看门狗同时具有多重身份:字符设备,混杂设备,平台设备。 
  634. *下面是看门狗驱动作为平台驱动的描述: 
  635. */  
  636. static struct platform_driver s3c2410wdt_driver = {  
  637.     .probe      = s3c2410wdt_probe,  
  638.     .remove     = __devexit_p(s3c2410wdt_remove),  
  639.     .shutdown   = s3c2410wdt_shutdown,  
  640.     .suspend    = s3c2410wdt_suspend,  
  641.     .resume     = s3c2410wdt_resume,  
  642.     .driver     = {  
  643.         .owner  = THIS_MODULE,  
  644.         .name   = "s3c2410-wdt",  
  645.     },  
  646. };  
  647.   
  648.   
  649. static char banner[] __initdata =  
  650.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";  
  651.   
  652.   
  653. /* 
  654. *看门狗驱动作为平台驱动的注册 
  655. */  
  656. static int __init watchdog_init(void)  
  657. {  
  658.     printk(banner);  
  659.     return platform_driver_register(&s3c2410wdt_driver);  
  660. }  
  661.   
  662. static void __exit watchdog_exit(void)  
  663. {  
  664.     platform_driver_unregister(&s3c2410wdt_driver);  
  665. }  
  666.   
  667. module_init(watchdog_init);  
  668. module_exit(watchdog_exit);  
  669.   
  670. MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "  
  671.           "Dimitry Andric <dimitry.andric@tomtom.com>");  
  672. MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");  
  673. MODULE_LICENSE("GPL");  
  674. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);  
  675. MODULE_ALIAS("platform:s3c2410-wdt");  

相关内容