一、开发环境
- 主 机:VMWare--Fedora 9
- 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4
- 编译器:arm-linux-gcc-4.3.2
二、相关概念
1、平台设备及平台设备驱动:
这个在前面篇幅:S3C2440上RTC时钟驱动开发实例讲解 中已经讲过了。这里只需了解一下系统为我们定义的看门狗(Watchdog)平台设备及资源情况,在arch/arm/plat-s3c24xx/devs.c中,如下:
de>/* Watchdog */de> de>/*定义了Watchdog平台设备使用的资源,这些资源在驱动程序中都会用到*/ de>/*定义了Watchdog平台设备*/ EXPORT_SYMBOL( s3c_device_wdt);de> |
2、混杂设备(misc设备)
misc设备是Linux定义的主设备号为10的特殊字符设备,因为不符合字符设备的范畴,所以被归纳为 misc设备,在Linux中有很多这种设备,例如:LED设备、Watchdog设备等等,系统会根据设备的次设备号来区分具体是哪个设备,通常这些次设备号被定义在include/linux/miscdevice.h中。在Linux中用miscdevice结构体来描述一个misc设备,这就意味着被定义为misc设备的驱动中就要实现该结构体中的接口函数。该结构体也定义在miscdevice.h中,如下:
de>struct miscdevice{ |
三、实例讲解
1、Watchdog硬件结构图分析:
我们从结构图和数据手册得知,看门狗Watchdog主要是实现系统自动复位的功能,他是利用芯片内部的定时器,定时输出连接到电路的复位端,程序在一定时间范围内对定时器清零(俗称“喂狗”),所以程序在正常工作时,定时器总是不能溢出,也就不能产生复位信号;如果程序出现错误,不在定时周期内复位看门狗,那么定时器就会溢出而产生复位信号使系统复位。
S3C2440的Watchdog模块提供了三个寄存器来对Watchdog进行操作,他们分别是:定时器控制寄存器WTCON、定时器数据寄存器 WTDAT和定时器计数寄存器WTCNT。注意:在对定时器数据寄存器WTDAT进行操作时必须在定时器控制寄存器WTCON使能之前写入一个计数目标值,当Watchdog使能开启后,WTDAT中的值会自动被加载到计数寄存器WTCNT中,然后Watchdog从CPU内部的时钟分频和时钟除数因子得到一个工作周期,当每个周期结束时计数寄存器WTCNT中的值会1,直到递减为0时,如果还不重新往WTCNT中写入新的计数目标值(即“喂狗”),则
Watchdog就产生复位信号使系统复位。关于这些寄存器的功能和寄存器的各个位的操作值请参考数据手册。
2、Watchdog驱动程序具体实现步骤(建立驱动文件my2440_watchdog.c):
注意:在每步中,为了让代码逻辑更加有条理和容易理解,就没有考虑代码的顺序,比如函数要先定义后调用。如果要编译此代码,请严格按照C语言的规范来调整代码的顺序。
①、 依然是驱动程序的最基本结构:Watchdog 驱动的初始化和卸载部分及其他,如下:
de>#include< de>de> de>/*Watchdog平台驱动结构体,平台驱动结构体定义在platform_device.h中,该结构体内的接口函数在第②、④步中实现*/ static int __init watchdog_init(void) static void __exit watchdog_exit(void) module_init( watchdog_init); /*驱动程序模块参数,如果在加载驱动模块时没有设定这些参数,则这些参数将采用默认值, MODULE_LICENSE( "GPL"); |
de>#include< linux/ /*定义了一个用来保存watchdog的IO端口占用的IO空间和经过虚拟映射后的内存地址*/ /*保存watchdog中断号,NO_IRQ宏定义在irq.h中*/ /*保存从平台时钟队列中获取watchdog的时钟*/ # define CONFIG_WATCHDOG_ATBOOT ( 0) /*申明并初始化一个自旋锁wdt_pie_lock,对Watchdog资源进行互斥访问*/ static int __devinit watchdog_probe(struct platform_device* pdev) /*在系统定义的watchdog平台设备中获取watchdog中断号 /*申请Watchdog中断服务,这里使用的是快速中断:IRQF_DISABLED。 /*获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致*/ /*从平台时钟队列中获取watchdog的时钟,这里为什么要取得这个时钟,因为看门狗定时器的工作周期是由这个 /*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/ /*申请watchdog的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别), /*将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。 /*好了,通过上面的步骤已经将watchdog的资源都准备好了,下面就开始使用啦*/ /*这里是计算并设置看门狗定时器时钟周期值,wdt_set_heartbeat定义在下面。符合数据手册中要求的“在看门狗定时 /*打印设置的值信息*/ /*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下: /*把看门狗设备又注册成为misc设备,misc_register定义在miscdevice.h中 /*函数wdt_start_or_stop定义在下面*/ return 0; err_noreq: err_nomap: return ret; /*看门狗定时器中断服务程序*/ return IRQ_HANDLED; /*看门狗定时器“喂狗”*/ writel( wdt_count, wdt_base+ S3C2410_WTCNT);/*往计数寄存器WTCNT重新写入计数值*/ spin_unlock( & wdt_lock);/*释放自旋锁,即解锁*/ /*计算并设置看门狗定时器时钟周期值并初始化看门狗定时器*/ if ( timeout< 1) freq / = 128; if ( if ((count tmr_margin = timeout; count /= divisor; wtcon = readl( wdt_base+ S3C2410_WTCON);/* wtcon=1000000000100001 /*设置看门狗定时器数据寄存器WTDAT的值,然后WTDAT的值会自动加载到WTCNT中*/ /*根据数据手册和上面计算的wtcon值可得,下面是设置看门狗定时控制寄存器WTCON为: return 0; /*根据标志flag的值来启动或者停止看门狗定时器,1表示启动,0表示停止*/ spin_lock( & wdt_pie_lock);/*获取自旋锁保护临界区资源*/ /*停止看门狗定时器,以下各寄存器的位操作请参照数据手册*/ if ( if ( soft_noboot) writel( wdt_count, wdt_base+ S3C2410_WTDAT); spin_unlock( & wdt_pie_lock);/*释放自旋锁,即解锁*/ |
③ 、 实现misc设备中对设备文件的操作,代码如下:
de>#include< linux/ /*申明并初始化一个信号量open_clock,对Watchdog资源进行互斥访问,注意:这里的信号量和第②步中的 /*用来表示Linux内核配置选项中配不配置CONFIG_WATCHDOG_NOWAYOUT项,WATCHDOG_NOWAYOUT定义在watchdog.h中*/ typedef enum close_state /*misc设备结构体实现*/ /*字符设备的相关操作实现*/ /*看门狗设备驱动的打开接口函数*/ if ( nowayout) /*开始记录看门狗定时器的当前操作状态为:无状态*/ /*启动看门狗定时器*/ /*表示返回的这个设备文件是不可以被seek操作的,nonseekable_open定义在fs.h中*/ /*看门狗设备驱动的关闭接口函数*/ /*恢复看门狗定时器的当前操作状态为:无状态*/ /*释放获取的信号量(即:解锁),与wdt_open中加锁相对应*/ return 0; /*看门狗设备驱动的写数据接口函数*/ |