Chinaunix首页 | 论坛 | 博客
  • 博客访问: 134951
  • 博文数量: 49
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 135
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-06 12:26
文章分类
文章存档

2015年(21)

2014年(5)

2012年(20)

2011年(3)

我的朋友

分类:

2012-12-07 14:51:57

第一部分: WDT驱动原理

WDT在内核中通常都实现为misc驱动。

WDT介绍

一个Watchdog TimerWDT)是一个在软件出错的时候可以复位计算机系统的硬件电路。

通常一个用户空间守护进程会在正常的时间间隔内通过/dev/watchdog特殊设备文件来通知内核的watchdog驱动,用户空间仍然正常。当这样的一个通知发生时,驱动通常会告诉硬件watchdog一切正常,然后watchdog应该再等待一段时间来复位系统。如果用户空间出问题(RAM错误,内核bug等),则通知将会停止,然后硬件watchdog将在超时后复位系统。

Linuxwatchdog API是一个相当特别的东西,不同的驱动实现是不同的,而且有时部分是不兼容的。这个文档正是要尝试着去说明已经出现的用法,并且使以后的驱动作者把它作为一份参考。

最简单的 API:

所有的设备驱动都支持的基本的操作模式,一旦/dev/watchdog被打开,则watchdog激活,并且除非喂狗,否则将在一段时间之后重启,这个时间被称为timeoutmargin。最简单的喂狗方法就是写一些数据到设备。一个非常简单的watchdog守护进程看起来就像这个文件这样:

Documentation/watchdog/src/watchdog-simple.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>

  5. int main(void)
  6. {
  7.     int fd = open("/dev/watchdog", O_WRONLY);
  8.     int ret = 0;
  9.     if (fd == -1) {
  10.         perror("watchdog");
  11.         exit(EXIT_FAILURE);
  12.     }
  13.     while (1) {
  14.         ret = write(fd, "\0", 1);
  15.         if (ret != 1) {
  16.             ret = -1;
  17.             break;
  18.         }
  19.         ret = fsync(fd);
  20.         if (ret)
  21.             break;
  22.         sleep(10);
  23.     }
  24.     close(fd);
  25.     return ret;
  26. }

一个高级一些的驱动在喂狗之前,可能还会做一些其他的事情,比如说检查HTTP服务器是否依然可以相应。

当设备关闭的时候,除非支持"Magic Close"特性。否则watchdog被关闭。这并不总是一个好主意,比如watchdog守护进程出现了bug并且崩溃了,则系统将不会重启。因此,某些驱动支持"Disable watchdog shutdown on close", CONFIG_WATCHDOG_NOWAYOUT配置选项。当编译内核的时候这个选项被设置为Y,则一旦watchdog被启动,则将没有办法能够停止。这样,则当watchdog守护进程崩溃的时候,系统仍将在超时后重启。Watchdog设备常常也支持nowayout模块参数,这样这个选项就可以在运行时进行控制。

Magic Close 特性:

如果一个驱动支持"Magic Close",则除非在关闭文件前,魔幻字符'V'被发送到/dev/watchdog,驱动将不停止watchdog。如果用户空间守护进程在关闭文件前没有发送这个字符,则驱动认为用户空间崩溃,并在关闭watchdog前停止喂狗。

这样的话,如果没有在一定的时间内重新打开watchdog,则将导致一个重启。

ioctl API:

所有标准的驱动也应该支持一个ioctl API

喂狗使用一个ioctl

所有的驱动都有一个ioctl接口支持至少一个ioctl命令,KEEPALIVE。这个 ioctl 做的事和一个写watchdog设备完全一样,所以,上面程序的主循环可以替换为:

  1. while (1) {

  2.        ioctl(fd, WDIOC_KEEPALIVE, 0);

  3.        sleep(10);

  4.     }

ioctl的参数被忽略。

设置和获得超时值:

对于某些驱动来说,在上层使用SETTIMEOUT ioctl命令改变watchdog的超时值是可能的,那些驱动在他们的选项与中有WDIOF_SETTIMEOUT标志。参数是一个代表以秒为单位的超时值,驱动将在同一个变量中返回实际使用的超时值,这个超时值可能由于硬件的限制,而不同于所请求的超时值

    int timeout = 45;

    ioctl(fd, WDIOC_SETTIMEOUT, &timeout);

    printf("The timeout was set to %d seconds\n", timeout);

如果设备的超时值的粒度只能到分钟,则这个例子可能实际打印"The timeout was set to 60 seconds"

自从Linux 2.4.18内核,通过GETTIMEOUT ioctl命令查询当前超时值也是可能的:

    ioctl(fd, WDIOC_GETTIMEOUT, &timeout);

    printf("The timeout was is %d seconds\n", timeout);

预处理:

Pretimeouts:

一些watchdog定时器,可以被设置为,在他们实际复位系统前,有一个触发。这可能通过一个NMI,中断,或其他机制。这将允许在它复位系统前Linux去记录一些有用的信息(比如panic信息和内核转储)。

    pretimeout = 10;

    ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);

注意,预超时值应该是一个相对于超时值提前的秒数。而不是直到预超时的秒数。

比如,如果你设置超时值为60秒,预超时值为10秒,那么预超时将在50秒后到达。设置为0则是禁用它。预超时还有一个get功能:

    ioctl(fd, WDIOC_GETPRETIMEOUT, &timeout);

    printf("The pretimeout was is %d seconds\n", timeout);

不是所有的watchdog驱动都支持一个预超时的。

获得重启前的秒数

一些watchdog驱动有一个报告在重启前的剩余时间的功能。WDIOC_GETTIMELEFT就是返回重启前的秒数的ioctl命令。

    ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);

    printf("The timeout was is %d seconds\n", timeleft);

环境监视:

Environmental monitoring:

所有的watchdog驱动都被要求返回更多关于系统的信息,有些返回温度,风扇和功率水平监测,依稀可以告诉你上一次重启系统的原因。GETSUPPORT ioctl可以用来查询设备可以做什么:

    struct watchdog_info ident;

    ioctl(fd, WDIOC_GETSUPPORT, &ident);

ident结构中返回的字段是:

        identity     一个标识watchdog驱动的字符串

    firmware_version 如果可用的话,就是卡的固件版本

    options           一个描述设备支持什么的标志

options字段可以有下面的位集,和描述GET_STATUS GET_BOOT_STATUS ioctls可以返回什么种类的信息。

第二部分: WDT驱动源码

驱动架构比较简单,由于kernel启动时,定义并加入了watchdog的platform_device,所以驱动定义并注册watchdog 的platform_driver
  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. #include <linux/module.h>
  26. #include <linux/moduleparam.h>
  27. #include <linux/types.h>
  28. #include <linux/timer.h>
  29. #include <linux/miscdevice.h>
  30. #include <linux/watchdog.h>
  31. #include <linux/fs.h>
  32. #include <linux/init.h>
  33. #include <linux/platform_device.h>
  34. #include <linux/interrupt.h>
  35. #include <linux/clk.h>
  36. #include <linux/uaccess.h>
  37. #include <linux/io.h>

  38. #include <mach/map.h>

  39. #undef S3C_VA_WATCHDOG
  40. #define S3C_VA_WATCHDOG (0)

  41. #include <plat/regs-watchdog.h>

  42. #define PFX "s3c2410-wdt: "

  43. #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
  44. #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)

  45. static int nowayout = WATCHDOG_NOWAYOUT;
  46. static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
  47. static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
  48. static int soft_noboot;
  49. static int debug;

  50. module_param(tmr_margin, int, 0);
  51. module_param(tmr_atboot, int, 0);
  52. module_param(nowayout, int, 0);
  53. module_param(soft_noboot, int, 0);
  54. module_param(debug, int, 0);

  55. MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="
  56.         __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
  57. MODULE_PARM_DESC(tmr_atboot,
  58.         "Watchdog is started at boot time if set to 1, default="
  59.             __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
  60. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  61.             __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  62. MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
  63.             "0 to reboot (default depends on ONLY_TESTING)");
  64. MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");

  65. static unsigned long open_lock;
  66. static struct device *wdt_dev; /* platform device attached to */
  67. static struct resource *wdt_mem;
  68. static struct resource *wdt_irq;
  69. static struct clk *wdt_clock;
  70. static void __iomem *wdt_base;
  71. static unsigned int wdt_count;
  72. static char expect_close;
  73. static DEFINE_SPINLOCK(wdt_lock);

  74. /* watchdog control routines */

  75. #define DBG(msg...) do { \
  76.     if (debug) \
  77.         printk(KERN_INFO msg); \
  78.     } while (0)
  79. /* functions */

  80. static void s3c2410wdt_keepalive(void)
  81. {
  82.     spin_lock(&wdt_lock);
  83.     writel(wdt_count, wdt_base + S3C2410_WTCNT);
  84.     spin_unlock(&wdt_lock);
  85. }

  86. static void __s3c2410wdt_stop(void)
  87. {
  88.     unsigned long wtcon;

  89.     wtcon = readl(wdt_base + S3C2410_WTCON);
  90.     wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
  91.     writel(wtcon, wdt_base + S3C2410_WTCON);
  92. }

  93. static void s3c2410wdt_stop(void)
  94. {
  95.     spin_lock(&wdt_lock);
  96.     __s3c2410wdt_stop();
  97.     spin_unlock(&wdt_lock);
  98. }

  99. static void s3c2410wdt_start(void)
  100. {
  101.     unsigned long wtcon;

  102.     spin_lock(&wdt_lock);

  103.     __s3c2410wdt_stop();

  104.     wtcon = readl(wdt_base + S3C2410_WTCON);
  105.     wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;

  106.     if (soft_noboot) {
  107.         wtcon |= S3C2410_WTCON_INTEN;
  108.         wtcon &= ~S3C2410_WTCON_RSTEN;
  109.     } else {
  110.         wtcon &= ~S3C2410_WTCON_INTEN;
  111.         wtcon |= S3C2410_WTCON_RSTEN;
  112.     }

  113.     DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
  114.         __func__, wdt_count, wtcon);

  115.     writel(wdt_count, wdt_base + S3C2410_WTDAT);
  116.     writel(wdt_count, wdt_base + S3C2410_WTCNT);
  117.     writel(wtcon, wdt_base + S3C2410_WTCON);
  118.     spin_unlock(&wdt_lock);
  119. }

  120. static int s3c2410wdt_set_heartbeat(int timeout)
  121. {
  122.     unsigned int freq = clk_get_rate(wdt_clock);
  123.     unsigned int count;
  124.     unsigned int divisor = 1;
  125.     unsigned long wtcon;
  126.     if (timeout < 1)
  127.         return -EINVAL;

  128.     freq /= 128;
  129.     count = timeout * freq;

  130.     DBG("%s: count=%d, timeout=%d, freq=%d\n",
  131.         __func__, count, timeout, freq);

  132.     /* if the count is bigger than the watchdog register,
  133.        then work out what we need to do (and if) we can
  134.        actually make this value
  135.     */

  136.     if (count >= 0x10000) {
  137.         for (divisor = 1; divisor <= 0x100; divisor++) {
  138.             if ((count / divisor) < 0x10000)
  139.                 break;
  140.         }

  141.         if ((count / divisor) >= 0x10000) {
  142.             dev_err(wdt_dev, "timeout %d too big\n", timeout);
  143.             return -EINVAL;
  144.         }
  145.     }

  146.     tmr_margin = timeout;

  147.     DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
  148.         __func__, timeout, divisor, count, count/divisor);

  149.     count /= divisor;
  150.     wdt_count = count;

  151.     /* update the pre-scaler */
  152.     wtcon = readl(wdt_base + S3C2410_WTCON);
  153.     wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
  154.     wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);

  155.     writel(count, wdt_base + S3C2410_WTDAT);
  156.     writel(wtcon, wdt_base + S3C2410_WTCON);

  157.     return 0;
  158. }

  159. /*
  160.  * /dev/watchdog handling
  161.  */

  162. static int s3c2410wdt_open(struct inode *inode, struct file *file)
  163. {
  164.     if (test_and_set_bit(0, &open_lock))
  165.         return -EBUSY;

  166.     if (nowayout)
  167.         __module_get(THIS_MODULE);

  168.     expect_close = 0;

  169.     /* start the timer */
  170.     s3c2410wdt_start();
  171.     return nonseekable_open(inode, file);
  172. }

  173. static int s3c2410wdt_release(struct inode *inode, struct file *file)
  174. {
  175.     /*
  176.      * Shut off the timer.
  177.      * Lock it in if it's a module and we set nowayout
  178.      */

  179.     if (expect_close == 42)
  180.         s3c2410wdt_stop();
  181.     else {
  182.         dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
  183.         s3c2410wdt_keepalive();
  184.     }
  185.     expect_close = 0;
  186.     clear_bit(0, &open_lock);
  187.     return 0;
  188. }

  189. static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
  190.                 size_t len, loff_t *ppos)
  191. {
  192.     /*
  193.      * Refresh the timer.
  194.      */
  195.     if (len) {
  196.         if (!nowayout) {
  197.             size_t i;

  198.             /* In case it was set long ago */
  199.             expect_close = 0;

  200.             for (i = 0; i != len; i++) {
  201.                 char c;

  202.                 if (get_user(c, data + i))
  203.                     return -EFAULT;
  204.                 if (c == 'V')
  205.                     expect_close = 42;
  206.             }
  207.         }
  208.         s3c2410wdt_keepalive();
  209.     }
  210.     return len;
  211. }

  212. #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)

  213. static const struct watchdog_info s3c2410_wdt_ident = {
  214.     .options = OPTIONS,
  215.     .firmware_version = 0,
  216.     .identity = "S3C2410 Watchdog",
  217. };


  218. static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,
  219.                             unsigned long arg)
  220. {
  221.     void __user *argp = (void __user *)arg;
  222.     int __user *p = argp;
  223.     int new_margin;

  224.     switch (cmd) {
  225.     case WDIOC_GETSUPPORT:
  226.         return copy_to_user(argp, &s3c2410_wdt_ident,
  227.             sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
  228.     case WDIOC_GETSTATUS:
  229.     case WDIOC_GETBOOTSTATUS:
  230.         return put_user(0, p);
  231.     case WDIOC_KEEPALIVE:
  232.         s3c2410wdt_keepalive();
  233.         return 0;
  234.     case WDIOC_SETTIMEOUT:
  235.         if (get_user(new_margin, p))
  236.             return -EFAULT;
  237.         if (s3c2410wdt_set_heartbeat(new_margin))
  238.             return -EINVAL;
  239.         s3c2410wdt_keepalive();
  240.         return put_user(tmr_margin, p);
  241.     case WDIOC_GETTIMEOUT:
  242.         return put_user(tmr_margin, p);
  243.     default:
  244.         return -ENOTTY;
  245.     }
  246. }

  247. /* kernel interface */

  248. static const struct file_operations s3c2410wdt_fops = {
  249.     .owner = THIS_MODULE,
  250.     .llseek = no_llseek,
  251.     .write = s3c2410wdt_write,
  252.     .unlocked_ioctl = s3c2410wdt_ioctl,
  253.     .open = s3c2410wdt_open,
  254.     .release = s3c2410wdt_release,
  255. };

  256. static struct miscdevice s3c2410wdt_miscdev = {
  257.     .minor = WATCHDOG_MINOR,
  258.     .name = "watchdog",
  259.     .fops = &s3c2410wdt_fops,
  260. };

  261. /* interrupt handler code */

  262. static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
  263. {
  264.     dev_info(wdt_dev, "watchdog timer expired (irq)\n");

  265.     s3c2410wdt_keepalive();
  266.     return IRQ_HANDLED;
  267. }
  268. /* device interface */

  269. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
  270. {
  271.     struct resource *res;
  272.     struct device *dev;
  273.     unsigned int wtcon;
  274.     int started = 0;
  275.     int ret;
  276.     int size;

  277.     DBG("%s: probe=%p\n", __func__, pdev);

  278.     dev = &pdev->dev;
  279.     wdt_dev = &pdev->dev;

  280.     /* get the memory region for the watchdog timer -- flags is IORESOURCE_MEM */
  281.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  282.     if (res == NULL) {
  283.         dev_err(dev, "no memory resource specified\n");
  284.         return -ENOENT;
  285.     }

  286.     size = (res->end - res->start) + 1;

  287.     //请求分配指定的I/O内存资源
  288.     wdt_mem = request_mem_region(res->start, size, pdev->name);
  289.     if (wdt_mem == NULL) {
  290.         dev_err(dev, "failed to get memory region\n");
  291.         ret = -ENOENT;
  292.         goto err_req;
  293.     }

  294.     //将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问
  295.     wdt_base = ioremap(res->start, size);
  296.     if (wdt_base == NULL) {
  297.         dev_err(dev, "failed to ioremap() region\n");
  298.         ret = -EINVAL;
  299.         goto err_req;
  300.     }

  301.     DBG("probe: mapped wdt_base=%p\n", wdt_base);

  302.     /* get the memory region for the watchdog timer -- flags is IORESOURCE_IRQ */
  303.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  304.     if (wdt_irq == NULL) {
  305.         dev_err(dev, "no irq resource specified\n");
  306.         ret = -ENOENT;
  307.         goto err_map;
  308.     }

  309.     //注册中断服务函数s3c2410wdt_irq()
  310.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
  311.     if (ret != 0) {
  312.         dev_err(dev, "failed to install irq (%d)\n", ret);
  313.         goto err_map;
  314.     }

  315.     //从平台时钟队列中获取clk
  316.     wdt_clock = clk_get(&pdev->dev, "watchdog");
  317.     if (IS_ERR(wdt_clock)) {
  318.         dev_err(dev, "failed to find watchdog clock source\n");
  319.         ret = PTR_ERR(wdt_clock);
  320.         goto err_irq;
  321.     }

  322.     //inform the system when the clock source should be running
  323.     clk_enable(wdt_clock);

  324.     /* see if we can actually set the requested timer margin, and if
  325.      * not, try the default value */

  326.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {
  327.         started = s3c2410wdt_set_heartbeat(
  328.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

  329.         if (started == 0)
  330.             dev_info(dev,
  331.                "tmr_margin value out of range, default %d used\n",
  332.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
  333.         else
  334.             dev_info(dev, "default timer value is out of range, "
  335.                             "cannot start\n");
  336.     }

  337.     ret = misc_register(&s3c2410wdt_miscdev);
  338.     if (ret) {
  339.         dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
  340.             WATCHDOG_MINOR, ret);
  341.         goto err_clk;
  342.     }

  343.     if (tmr_atboot && started == 0) {
  344.         dev_info(dev, "starting watchdog timer\n");
  345.         s3c2410wdt_start();
  346.     } else if (!tmr_atboot) {
  347.         /* if we're not enabling the watchdog, then ensure it is
  348.          * disabled if it has been left running from the bootloader
  349.          * or other source */

  350.         s3c2410wdt_stop();
  351.     }

  352.     /* print out a statement of readiness */

  353.     wtcon = readl(wdt_base + S3C2410_WTCON);

  354.     dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
  355.          (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
  356.          (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
  357.          (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");

  358.     return 0;

  359.  err_clk:
  360.     clk_disable(wdt_clock);
  361.     clk_put(wdt_clock);

  362.  err_irq:
  363.     free_irq(wdt_irq->start, pdev);

  364.  err_map:
  365.     iounmap(wdt_base);

  366.  err_req:
  367.     release_resource(wdt_mem);
  368.     kfree(wdt_mem);

  369.     return ret;
  370. }

  371. static int __devexit s3c2410wdt_remove(struct platform_device *dev)
  372. {
  373.     release_resource(wdt_mem);
  374.     kfree(wdt_mem);
  375.     wdt_mem = NULL;

  376.     free_irq(wdt_irq->start, dev);
  377.     wdt_irq = NULL;

  378.     clk_disable(wdt_clock);
  379.     clk_put(wdt_clock);
  380.     wdt_clock = NULL;

  381.     iounmap(wdt_base);
  382.     misc_deregister(&s3c2410wdt_miscdev);
  383.     return 0;
  384. }

  385. static void s3c2410wdt_shutdown(struct platform_device *dev)
  386. {
  387.     s3c2410wdt_stop();
  388. }

  389. #ifdef CONFIG_PM

  390. static unsigned long wtcon_save;
  391. static unsigned long wtdat_save;

  392. static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
  393. {
  394.     /* Save watchdog state, and turn it off. */
  395.     wtcon_save = readl(wdt_base + S3C2410_WTCON);
  396.     wtdat_save = readl(wdt_base + S3C2410_WTDAT);

  397.     /* Note that WTCNT doesn't need to be saved. */
  398.     s3c2410wdt_stop();

  399.     return 0;
  400. }

  401. static int s3c2410wdt_resume(struct platform_device *dev)
  402. {
  403.     /* Restore watchdog state. */

  404.     writel(wtdat_save, wdt_base + S3C2410_WTDAT);
  405.     writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
  406.     writel(wtcon_save, wdt_base + S3C2410_WTCON);

  407.     printk(KERN_INFO PFX "watchdog %sabled\n",
  408.            (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");

  409.     return 0;
  410. }
  411. #else
  412. #define s3c2410wdt_suspend NULL
  413. #define s3c2410wdt_resume NULL
  414. #endif /* CONFIG_PM */


  415. /*
  416.  *platform_driver s3c2410wdt_driver 与 platform_device s3c_device_wdt 对应
  417.  *s3c_device_wdt 在arch/arm/plat-s3c24xx/devs.c中定义
  418.  *两者的工作顺序是先定义platform_device -> 注册 platform_device->
  419.  *在mini2440_machine_init()中完成
  420.  *再定义 platform_driver-> 注册 platform_driver
  421.  */
  422. static struct platform_driver s3c2410wdt_driver = {
  423.     .probe = s3c2410wdt_probe, //设备的检测,所以需要先注册设备
  424.     .remove = __devexit_p(s3c2410wdt_remove), //删除该设备
  425.     .shutdown = s3c2410wdt_shutdown, //关闭该设备
  426.     .suspend = s3c2410wdt_suspend,
  427.     .resume = s3c2410wdt_resume,
  428.     .driver = { //设备驱动
  429.         .owner = THIS_MODULE,
  430.         /*
             *对应 struct platform_device s3c_device_wdt = {
             *     .name         = "s3c2410-wdt",
             *       ...
             *    };
             */
  431.         .name = "s3c2410-wdt",
  432.     },
  433. };


  434. static char banner[] __initdata =
  435.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";

  436. static int __init watchdog_init(void) //模块初始化
  437. {
  438.     printk(banner); //打印信息
  439.     return platform_driver_register(&s3c2410wdt_driver); //注册设备的驱动程序
  440. }

  441. static void __exit watchdog_exit(void) //移除模块
  442. {
  443.     platform_driver_unregister(&s3c2410wdt_driver); //unregister a driver for platform-level devices
  444. }

  445. module_init(watchdog_init);
  446. module_exit(watchdog_exit);

  447. MODULE_AUTHOR("Ben Dooks , "
  448.           "Dimitry Andric ");
  449. MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
  450. MODULE_LICENSE("GPL");
  451. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  452. MODULE_ALIAS("platform:s3c2410-wdt");

阅读(931) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~