目标平台: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- static struct platform_driver s3c2410wdt_driver = {
- .probe = s3c2410wdt_probe,
- .remove = s3c2410wdt_remove,
- .shutdown = s3c2410wdt_shutdown,
- .suspend = s3c2410wdt_suspend,
- .resume = s3c2410wdt_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2410-wdt",
- },
- };
-
- static char banner[] __initdata =
- KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics
";
-
- static int __init watchdog_init(void){printk(banner);return platform_driver_register(&s3c2410wdt_driver);}
-
- module_init(watchdog_init)
模块的注册函数很简单,直接调用了 platform的驱动注册函数platform_driver_register。 该函数在注册时会调用驱动的probe方法,也即s3c2410wdt_probe函数。 我们来看下这个函数:
[cpp] view plain copy- static int s3c2410wdt_probe(struct platform_device *pdev)
- {
- struct resource *res;
- struct device *dev;
- unsigned int wtcon;
- int started = 0;
- int ret;
- int size;
-
- DBG("%s: probe=%p
", __func__, pdev);
-
- dev = &pdev->dev;
- wdt_dev = &pdev->dev;
-
-
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "no memory resource specified
");
- return -ENOENT;
- }
-
-
- size = (res->end - res->start) + 1;
- wdt_mem = request_mem_region(res->start, size, pdev->name);
- if (wdt_mem == NULL) {
- dev_err(dev, "failed to get memory region
");
- ret = -ENOENT;
- goto err_req;
- }
-
-
- wdt_base = ioremap(res->start, size);
- if (wdt_base == NULL) {
- dev_err(dev, "failed to ioremap() region
");
- ret = -EINVAL;
- goto err_req;
- }
-
- DBG("probe: mapped wdt_base=%p
", wdt_base);
-
-
- wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (wdt_irq == NULL) {
- dev_err(dev, "no irq resource specified
");
- ret = -ENOENT;
- goto err_map;
- }
-
-
- ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
- if (ret != 0) {
- dev_err(dev, "failed to install irq (%d)
", ret);
- goto err_map;
- }
-
- wdt_clock = clk_get(&pdev->dev, "watchdog");
- if (IS_ERR(wdt_clock)) {
- dev_err(dev, "failed to find watchdog clock source
");
- ret = PTR_ERR(wdt_clock);
- goto err_irq;
- }
-
-
- clk_enable(wdt_clock);
-
-
-
-
-
- if (s3c2410wdt_set_heartbeat(tmr_margin)) {
- started = s3c2410wdt_set_heartbeat(
- CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
-
- if (started == 0)
- dev_info(dev,
- "tmr_margin value out of range, default %d used
",
- CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
- else
- dev_info(dev, "default timer value is out of range, cannot start
");
- }
-
-
- ret = misc_register(&s3c2410wdt_miscdev);
- if (ret) {
- dev_err(dev, "cannot register miscdev on minor=%d (%d)
",
- WATCHDOG_MINOR, ret);
- goto err_clk;
- }
-
-
-
-
-
-
- if (tmr_atboot && started == 0) {
- dev_info(dev, "starting watchdog timer
");
- s3c2410wdt_start();
- } else if (!tmr_atboot) {
-
-
-
-