WATCHDOG 简介
1:硬件看门狗(watchdog timer)是一个定时器,其定时输出连接到电路的复位端。在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。
2:当看门狗启动后,计数器开始自动计数,在计数器溢出前如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称 “被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗“),不让复位信号产生。如果系统不出问题,程序能够按时“喂狗”。一旦程序跑飞,没有“喂狗”,系统“被咬” 复位。
- CPU内部自带的看门狗(本文分析的重点):此类看门狗一般是将一个芯片中的定时器来作为看门狗,通过程序的初始化,写入初值,设定溢出时间,并启动定时器。程序按时对定时器赋初值(或复位),以免被咬。这种看门狗是可以被禁用的(只要停止这个定时器即可),好比对那只要咬你的狗来个“葵花点穴手”。大部分CPU都内置看门狗,硬件原理可参考各芯片数据手册。
优点:可以通过程序改变溢出时间;可以随时禁用
缺点:需要初始化;如果程序在初始化、启动完成前跑飞或在禁用后跑飞,看门狗就无法复位系统,这样看门狗的作用就没有了,系统恢复能力降低。
2、独立的看门狗芯片:这种看门狗主要有一个用于喂狗的引脚(一般与CPU的GPIO相连)和一个复位引脚(与系统的RESET引脚相连),如果没有在一定时间内改变喂狗脚的电平,复位引脚就会改变状态复位CPU。此类看门狗一上电就开始工作,无法禁用。现在常用的芯片有:CAT705/CAT706、IMP706等等,溢出时间在1.6秒左右。 硬件原理可以参考各芯片数据手册和《基于Linux的嵌入式系统全程喂狗策略》。
优点:无须配置,上电即用。无法禁用,系统必须按时喂狗,系统恢复能力高。
缺点:无法灵活配置溢出时间,无法禁用,灵活性降低。
看门狗驱动涉及两种设备模型,分别是平台设备和混杂设备
平台设备模型(本文分析的重点):
从Linux2.6起引入了一套新的驱动管理和注册模型,即平台设备platform_device和平台驱动platform_driver.Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver表示,平台设备模型与传统的device和driver模型相比,一个十分明显的优势在于平台设备模型将设备本身的资源注册进内核,由内核统一管理。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性
混杂设备:
混杂设备并没有一个明确的定义。由于设备号比较紧张,所以一些不相关的设备可以使用同一主设备号。主设备号通常是10.由于这个原因,一些设备也可以叫做混杂设备。
1:看门狗的platform资源
/* watch dog */
#ifdef CONFIG_GSC3290_WDT
static struct resource gsc3290_wdt_resources[] = {
{
.start = GSC3290_BASEADDR_WDT & 0x1fffffff,
.end = (GSC3290_BASEADDR_WDT & 0x1fffffff ) + 0x100,
.flags = IORESOURCE_MEM,
},
{
.start = EXT_GSC3290_WATCHDOG_IRQ,
.end = EXT_GSC3290_WATCHDOG_IRQ,
.flags = IORESOURCE_IRQ
},
};
static struct platform_device gsc3290_wdt_device = {
.name = "gsc3290-watchdog",
.id = -1,
.num_resources = ARRAY_SIZE(gsc3290_wdt_resources),
.resource = gsc3290_wdt_resources,
};
#endif
2:Platfoem结构体定义
/*platform结构体的定义*/
static struct platform_driver gsc3290wdt_driver = {
.probe = gsc3290wdt_probe,
.remove = __devexit_p(gsc3290wdt_remove),
.shutdown = gsc3290wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "gsc3290-watchdog",
.pm = &gsc3290wdt_pm_ops,
},
};
3:probe函数
static int __devinit gsc3290wdt_probe(struct platform_device *pdev)
{
int ret = 0, size = 0;
struct gsc3290_wdt *gsc = NULL;
/*1:模块私有结构体的申请*/
gsc = kzalloc(sizeof(struct gsc3290_wdt), GFP_KERNEL);
if (!gsc) {
DBG("no memory error!!!!\n");
return -ENOMEM;
}
/* Initialize the driver data */
gsc->suspend_flg = 0;
gsc->dev = &pdev->dev;
/*2:锁成员的初始化*/
spin_lock_init(&gsc->wdt_lock);
/*3:链表成员的初始化*/
INIT_LIST_HEAD(&gsc->device_entry);
/*4:看门狗名称的初始化*/
strlcpy(gsc->name, GSC3290_WDT_NAME, sizeof(gsc->name));
/* get the memory region for the watchdog timer */
/*5:获取platform的内存资源*/
gsc->wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (gsc->wdt_mem == NULL) {
ret = -ENOENT;
goto free;
}
size = resource_size(gsc->wdt_mem);
if (!request_mem_region(gsc->wdt_mem->start, size, pdev->name)) {
ret = -EBUSY;
goto free;
}
/*6:内存空间的申请和映射,获取寄存器操作的基地址*/
gsc->wdt_base = ioremap(gsc->wdt_mem->start, size);
if (gsc->wdt_base == NULL) {
ret = -EINVAL;
goto err_req;
}
/*7:获取platform的中断资源*/
gsc->wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (gsc->wdt_irq == NULL) {
DBG("no irq resource specified!!!!\n");
ret = -ENOENT;
goto err_map;
}
/*8:看门狗中断函数的注册*/
ret = request_irq(gsc->wdt_irq->start, gsc3290wdt_irq, 0, pdev->name, gsc);
if (ret != 0) {
DBG("failed to install irq (%d)!!!!\n", ret);
goto err_map;
}
/*9:clk模块的操作,clk_get从一个时钟list链表中以字符id名称来查找一个时钟clk结构体并且返回,最后调用clk.enable(),来时能对应的外设时钟源*/
gsc->clk = clk_get(NULL, "wdt");
if (IS_ERR(gsc->clk)) {
ret = PTR_ERR(gsc->clk);
goto err_irq;
}
clk_enable(gsc->clk);
/*10:看门狗混杂设备的注册,就是封装好的字符设备,创建的设备在/dev目录下*/
ret = misc_register(&gsc3290wdt_miscdev);
if(ret < 0) {
DBG("misc register watchdog error!!!!\n");
goto err_clk;
}
#ifdef CONFIG_PM
/* set pm runtime power state and register with power system */
pm_runtime_set_active(gsc->dev);
pm_runtime_enable(gsc->dev);
pm_runtime_resume(gsc->dev);
//device_init_wakeup(gsc->dev, 1);
#endif
/*11:看门狗模块加入到全局看门狗链表当中*/
mutex_lock(&wdt_device_list_lock);
list_add(&gsc->device_entry, &wdt_device_list);
mutex_unlock(&wdt_device_list_lock);
/*12:模块私有数据的设置*/
platform_set_drvdata(pdev, gsc);
printk(KERN_INFO "gsc3290 watchdog probe success\n");
DBG("############\n");
return ret;
err_clk:
clk_disable(gsc->clk);
clk_put(gsc->clk);
err_irq:
free_irq(gsc->wdt_irq->start, pdev);
err_map:
iounmap(gsc->wdt_base);
err_req:
release_mem_region(gsc->wdt_mem->start, size);
gsc->wdt_mem = NULL;
free:
kfree(gsc);
printk(KERN_INFO "!!!!!!gsc3290 watchdog probe err!!!!!!\n");
return ret;
}
4:ioctl函数完成和用户态数据的交互
static long gsc3290wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int new_margin;
void __user *argp = (void __user *)arg;
int __user *p = argp;
struct gsc3290_wdt *wdt;
/*参数的范围的判断*/
if (_IOC_TYPE(cmd) != WDT_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > WDT_IOC_MAXNR)
return -ENOTTY;
wdt = file->private_data;
switch (cmd) {
case WDT_START:
printk(KERN_INFO"START WDT...\n");
gsc3290wdt_start(wdt);
return 0;
case WDT_KEEPALIVE:
/*keep live就是完后喂狗的操作*/
gsc3290wdt_keepalive(wdt);
return 0;
case WDT_PAUSE:
/*看门狗模块的暂停*/
gsc3290wdt_stop(wdt);
return 0;
case WDT_READ:
/*dump出模块相关的寄存器*/
ReadDebug(wdt);
return 0;
case WDT_SETTIMEOUT:
/*设置看门狗的超时时间*/
if (get_user(new_margin, p))
return -EFAULT;
wdt->pclkOne= new_margin & 0x000f;
wdt->torr = (new_margin >> 4) & 0xffff;
gsc3290wdt_set_par(wdt);
gsc3290wdt_keepalive(wdt);
return 0;
case WDT_GETTIMEOUT:
/*获取看门狗的超时时间*/
return put_user(gsc3290wdt_read_par(wdt), p);
default:
return -ENOTTY;
}
}
综合:看门狗驱动就是借助platform的简单的字符设备驱动,在使用的时候关键分析的还是gsc3290wdt_fops函数操作结构体,该驱动只是一个简单的字符设备,比较简单