来个简单的看门狗驱动——静态平台类的杂设备看门狗驱动,有定时和重启两个基本功能。
一、S3C2440中的看门狗——具体请看s3c2440文档
看门狗应该可以算是S3C2440中最简单的一个设备的,仅仅只有三个寄存器需要配置。S3C2440中的看门狗有两种功能(只能二选一):
1、复位功能,在指定时间内,如果没有执行喂狗操作。指定时间到达后会执行系统重启操作。
2、定时功能,每隔指定的时间,看门狗就会往处理器发送中断,执行中断处理函数。
接下来就要看看控制看门狗的三个寄存器:
1、控制寄存器——WTCON:
首先要看看分频系数Prescaler value[15:8]和时钟分频Clock select[4:3]。看门狗的时钟频率就是通过这两个值分频得出的:
t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ]
每个(1/t_watchdog)秒,看门狗计数器减一。
Watchdog timer[5]是看门狗的是能控制位,初始化时必须将此为置一,不然看门狗无法操作。
Interrupt generation[2]是控制中断的产生,如果你使用看门狗来执行定时功能,置一代表使能看门狗产生中断,反之不产生中断。
Reset enable/disable[0]用控制看门狗是否复位,如果置一,代表当时间到达后系统重启。
2、数据寄存器WTDAT和计数寄存器WTCNT
这两个都是用于存放16位数据的寄存器。
1、如果是复位模式,从WTCON[5]置一开始,每隔(1/t_watchdog)秒,WTCNT中的值减一,直到WTCNT为0,系统就会复位。在WTCNT还没有为0前,可以重新往WTCNT中赋值,这样WTCNT就会从新的数值开始减一,这就是所谓的喂狗操作,重复地在WTCNT变0前喂狗,就可以让系统不复位。
2、如果是定时模式, 从WTCON[5]置一开始,每隔(1/t_watchdog)秒,WTCNT中的值减一,直到WTCNT为0,看门狗往处理器发送中断信号,并自动将WTDAT赋值给WTCNT,让WTCNT重新开始减一。这样的重复操作就实现了看门狗的定时。
注意:不管是哪一种模式,一开始时看门狗都不会将WTDAT的值赋给WTCNT,都是直接以WTCNT的值开始计数。所以,在是能看门狗之前,必须先设备WTCNT的值,指定时间长短。
接下来的程序我需要的时间间隔是1秒,所以,Prescaler value[15:8]我赋值为99,Clock select[4:3]我赋值为128。S3C2440中的PCLK定义为50000000,通过公式可以计算出:
t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ] = 3906.25
所以,看门狗的计数间隔是(1/3906.25)秒,1秒需要计数大约3906次。
看门狗介绍完毕,接着就开始写驱动了,在写驱动前贴张之前我介绍过的图,我写驱动的步骤:
接在来我就会按这个顺序来写个简单的看门狗驱动,实现看门狗的两种功能,定时和复位。
二、第一个看门狗程序
写驱动前先要写一些基本的操作代码,检验一下该设备是否能正常工作。
第一个看门狗程序做了三步,简单实现了看门狗的复位操作:
1、定义了一个维护看门狗数据的结构体:
9 struct _wdt_t {
10 unsigned long phys, virt; //存放物理地址和对应的虚拟地址
11 unsigned long wtcon, wtdat, wtcnt; //存放寄存器
12 unsigned long reg;
13
14 void (*init_reset)(struct _wdt_t *); //操作看门狗的函数指针
15 };
2、实现了看门狗的复位操作:
19 static void s3c_wdt_init_reset(struct _wdt_t *wdt)
20 {
21 iowrite32((int)(WDT_1S * 10), wdt->wtdat); //其实这个不设置也可以
22 iowrite32((int)(WDT_1S * 10), wdt->wtcnt); //设置10秒后系统复位
23 iowrite32(0x6339, wdt->wtcon); //设置wtcon寄存器为0x6339,使能了看门狗和复位功能
24 }
3、封装了设备的初始化和注销函数:
26 int init_wdt_device(struct _wdt_t *wdt)
27 {
28 int ret = 0;
29 //ioremap
30 wdt->phys = 0x53000000;
31 wdt->virt = (unsigned long)ioremap(wdt->phys, 0x0c);
32 wdt->wtcon = wdt->virt + 0x0;
33 wdt->wtdat = wdt->virt + 0x4;
34 wdt->wtcnt = wdt->virt + 0x8;
35
36 //function
37 wdt->init_reset = s3c_wdt_init_reset;