S3C2440上看门狗(Watchdog)驱动开发实例讲解
来源:互联网 发布:建筑施工仿真软件 编辑:程序博客网 时间:2024/06/05 13:59
嵌入式Linux之我行——S3C2440上看门狗(Watchdog)驱动开发实例讲解
一、开发环境
- 主 机: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平台设备使用的资源,这些资源在驱动程序中都会用到*/
static struct resource s3c_wdt_resource[] = {
[ 0] = { /*Watchdog所使用IO端口资源范围*/
. start = S3C24XX_PA_WATCHDOG,
. end = S3C24XX_PA_WATCHDOG+ S3C24XX_SZ_WATCHDOG - 1,
. flags = IORESOURCE_MEM,
} ,
[ 1] = { /*Watchdog中断资源*/
. start = IRQ_WDT,
. end = IRQ_WDT,
. flags = IORESOURCE_IRQ,
}
} ;
de>
de>/*定义了Watchdog平台设备*/
struct platform_device s3c_device_wdt = {
. name = "s3c2410-wdt" , /*设备名称*/
. id = - 1,
. num_resources = ARRAY_SIZE( s3c_wdt_resource), /*资源数量*/
. resource = s3c_wdt_resource, /*引用上面定义的资源*/
} ;
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{
int minor;
const char* name;
const struct file_operations* fops;
struct list_head list ;
struct device * parent;
struct device * this_device;
} ; de>
三、实例讲解
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 < linux/ kernel. h>
# include < linux/ module. h>
# include < linux/ init. h>
# include < linux/ platform_device. h>de>
de>de>
de>/*Watchdog平台驱动结构体,平台驱动结构体定义在platform_device.h中,该结构体内的接口函数在第②、④步中实现*/
static struct platform_driver watchdog_driver=
{
. probe = watchdog_probe, /*Watchdog探测函数,在第②步中实现*/
. remove = __devexit_p( watchdog_remove), /*Watchdog移除函数, 在第④步中实现*/
. shutdown = watchdog_shutdown, /*Watchdog关闭函数,在第④步中实现*/
. suspend = watchdog_suspend, /*Watchdog挂起函数,在第④步中实现*/
. resume = watchdog_resume, /*Watchdog恢复函数,在第④步中实现*/
. driver =
{
/*注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来*/
. name ="s3c2410-wdt" ,
. owner = THIS_MODULE,
} ,
} ;
static int __init watchdog_init(void )
{
/*将Watchdog注册成平台设备驱动*/
return platform_driver_register(& watchdog_driver);
}
static void __exit watchdog_exit(void )
{
/*注销Watchdog平台设备驱动*/
platform_driver_unregister( & watchdog_driver) ;
}
module_init( watchdog_init);
module_exit( watchdog_exit);
/*驱动程序模块参数,如果在加载驱动模块时没有设定这些参数,则这些参数将采用默认值,
这些参数在接下来的步骤中将被一一用到,参数具体作用也将在各步骤中来说明*/
module_param( tmr_margin,int , 0);
module_param( tmr_atboot,int , 0);
module_param( nowayout, int , 0);
module_param( soft_noboot,int , 0);
MODULE_LICENSE( "GPL") ;
MODULE_AUTHOR( "Huang Gang") ;
MODULE_DESCRIPTION( "My2440 Watchdog Driver" ) ; de>
de>#include < linux/ irq. h>
# include < asm / io. h>
# include < linux/ clk. h>
# include < linux/ ioport. h>
# include < plat/ regs- watchdog. h>
# include < linux/ miscdevice. h>
/*定义了一个用来保存watchdog的IO端口占用的IO空间和经过虚拟映射后的内存地址*/
static struct resource* wdt_mem;
static void __iomem* wdt_base;
/*保存watchdog中断号,NO_IRQ宏定义在irq.h中*/
static int wdt_irqno= NO_IRQ;
/*保存从平台时钟队列中获取watchdog的时钟*/
static struct clk* wdt_clock;
# define CONFIG_WATCHDOG_ATBOOT ( 0)
# define CONFIG_WATCHDOG_DEFAULT_TIME ( 15)
static int tmr_atboot= CONFIG_WATCHDOG_ATBOOT;
static int tmr_margin= CONFIG_WATCHDOG_DEFAULT_TIME;
static int soft_noboot;
static unsignedint wdt_count;/*用于保存经计算后得到的计数寄存器WTCNT的计数值*/
/*申明并初始化一个自旋锁wdt_pie_lock,对Watchdog资源进行互斥访问*/
static DEFINE_SPINLOCK( wdt_pie_lock);
static int __devinit watchdog_probe(struct platform_device * pdev)
{
int ret;
int started = 0;
struct resource * res; /*定义一个资源,用来保存获取的watchdog的IO资源*/
/*在系统定义的watchdog平台设备中获取watchdog中断号
platform_get_irq定义在platform_device.h中*/
wdt_irqno = platform_get_irq( pdev, 0);
if ( wdt_irqno< 0)
{
/*获取watchdog中断号不成功错误处理
dev_err定义在device.h中,在platform_device.h中已经引用,所以这里就不需再引用了*/
dev_err( & pdev-> dev, "no irq for watchdog/n" ) ;
return - ENOENT;
}
/*申请Watchdog中断服务,这里使用的是快速中断:IRQF_DISABLED。
中断服务程序为:wdt_irq,将Watchdog平台设备pdev做参数传递过去了*/
ret = request_irq( wdt_irqno, wdt_irq, IRQF_DISABLED, pdev-> name, pdev);
if ( ret)
{
/*错误处理*/
dev_err( dev,"IRQ%d error %d/n" , wdt_irqno, ret);
return ret;
}
/*获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致*/
res = platform_get_resource( pdev, IORESOURCE_MEM, 0);
if ( res= = NULL )
{
/*错误处理*/
dev_err( & pdev-> dev, "failed to get memory region resource/n" );
return - ENOENT;
}
/*从平台时钟队列中获取watchdog的时钟,这里为什么要取得这个时钟,因为看门狗定时器的工作周期是由这个
时钟和时钟除数因子得到的。注意这里的watchdog参数要与系统中定义的时钟名称一致才能获取得到,也就是
说,系统必须先定义得有watchdog。系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/
wdt_clock = clk_get(& pdev- > dev, "watchdog") ;
if ( IS_ERR( wdt_clock))
{
/*错误处理*/
dev_err( & pdev-> dev, "failed to find watchdog clock source/n" );
return PTR_ERR( wdt_clock);
}
/*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/
clk_enable( wdt_clock);
/*申请watchdog的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),
request_mem_region定义在ioport.h中*/
wdt_mem = request_mem_region( res-> start, res-> end - res-> start + 1, pdev-> name) ;
if ( wdt_mem= = NULL )
{
/*错误处理*/
dev_err( & pdev-> dev, "failed to reserve memory region/n" ) ;
ret = - ENOENT;
goto err_noclk;
}
/*将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/
wdt_base = ioremap( res-> start, res-> end - res-> start + 1);
if ( wdt_base= = NULL )
{
/*错误处理*/
dev_err( & pdev-> dev, "failed ioremap()/n" ) ;
ret = - EINVAL;
goto err_noreq;
}
/*好了,通过上面的步骤已经将watchdog的资源都准备好了,下面就开始使用啦*/
/*这里是计算并设置看门狗定时器时钟周期值,wdt_set_heartbeat定义在下面。符合数据手册中要求的“在看门狗定时
器开始工作之前,一个初始值必须先写入看门狗定时器计数寄存器WTCNT中”。其实这里就是初始化看门狗定时器*/
if ( wdt_set_heartbeat( pdev, tmr_margin))
{
/*这里调用两次的意思是看能不能设置成期望的值,如果不能就设置默认的值*/
started = wdt_set_heartbeat( pdev, CONFIG_WATCHDOG_DEFAULT_TIME);
/*打印设置的值信息*/
if ( started= = 0)
dev_info( & pdev- > dev,"tmr_margin value out of range, default %d used/n", CONFIG_WATCHDOG_DEFAULT_TIME);
else
dev_info( & pdev- > dev,"default timer value is out of range, cannot start/n") ;
}
/*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:
static inline void device_init_wakeup(struct device *dev, int val){
dev->power.can_wakeup = dev->power.should_wakeup = !!val;}
显然这个函数是让驱动支持电源管理的,这里只要知道,can_wakeup为1时表明这个设备可以被唤醒,设备驱动为了支持
Linux中的电源管理,有责任调用device_init_wakeup()来初始化can_wakeup,而should_wakeup则是在设备的电源状态
发生变化的时候被device_may_wakeup()用来测试,测试它该不该变化,因此can_wakeup表明的是一种能力,
而should_wakeup表明的是有了这种能力以后去不去做某件事。好了,我们没有必要深入研究电源管理的内容了,
要不就扯远了,电源管路以后再讲*/
device_init_wakeup( & pdev- > dev, 1);
/*把看门狗设备又注册成为misc设备,misc_register定义在miscdevice.h中
wdt_miscdev结构体定义及内部接口函数在第③步中讲*/
ret = misc_register(& wdt_miscdev);
if ( ret)
{
/*错误处理*/
dev_err( & pdev-> dev, "cannot register miscdev on minor=%d (%d)/n" , WATCHDOG_MINOR, ret);
goto err_nomap;
}
/*函数wdt_start_or_stop定义在下面*/
if ( tmr_atboot& & started= = 0)
{
wdt_start_or_stop( 1); /*参数1表示启动看门狗定时器*/
}
else if( ! tmr_atboot)
{
wdt_start_or_stop( 0); /*参数0表示停止看门狗定时器*/
}
return 0;
//以下是上面错误处理的跳转点
err_noclk:
clk_disable( wdt_clock);
clk_put( wdt_clock);
err_noreq:
release_resource( wdt_mem);
kfree( wdt_mem);
err_nomap:
iounmap( wdt_base);
return ret;
}
/*看门狗定时器中断服务程序*/
static irqreturn_t wdt_irq(int irq, void * argv)
{
/*主要要做的事情是在看门狗定时器计数寄存器值递减到0之前重新写入新值(即:“喂狗”)*/
wdt_keepalive( ) ;
return IRQ_HANDLED;
}
/*看门狗定时器“喂狗”*/
static void wdt_keepalive(void )
{
spin_lock( & wdt_lock); /*获取自旋锁保护临界区资源*/
writel( wdt_count, wdt_base+ S3C2410_WTCNT); /*往计数寄存器WTCNT重新写入计数值*/
spin_unlock( & wdt_lock); /*释放自旋锁,即解锁*/
}
/*计算并设置看门狗定时器时钟周期值并初始化看门狗定时器*/
static int wdt_set_heartbeat(struct platform_device * pdev, int timeout)
{
unsigned int freq= clk_get_rate( wdt_clock);
unsigned intcount ;
unsigned int divisor= 1;
unsigned long wtcon;
if ( timeout< 1)
return - EINVAL;
freq / = 128;
count = timeout* freq;
if ( count > = 0x10000)
{
for ( divisor= 1; divisor< = 0x100; divisor++ )
{
if (( count / divisor) < 0x10000)
break ;
}
if (( count / divisor) > = 0x10000)
{
dev_err( & pdev- > dev,"timeout %d too big/n" , timeout) ;
return - EINVAL;
}
}
tmr_margin = timeout;
count /= divisor;
wdt_count = count;
wtcon = readl( wdt_base+ S3C2410_WTCON); /* wtcon=1000000000100001 这是控制寄存器的默认值,看数据手册得到*/
wtcon & =~ S3C2410_WTCON_PRESCALE_MASK;/* wtcon=1000000000100001 & ~1111111100000000 = 0000000000100001 */
/*S3C2410_WTCON_PRESCALE宏是将divisor-1的值向左位移8位,也就是说该值的右8位都为0,则计算如下:
wtcon=0000000000100001 | (xxxxxxxx)00000000 = (xxxxxxxx)00100001*/
wtcon | = S3C2410_WTCON_PRESCALE( divisor- 1);
/*设置看门狗定时器数据寄存器WTDAT的值,然后WTDAT的值会自动加载到WTCNT中*/
writel( count, wdt_base + S3C2410_WTDAT);
/*根据数据手册和上面计算的wtcon值可得,下面是设置看门狗定时控制寄存器WTCON为:
看门狗定时器输出使能有效、一个保留位默认0、中断使能无效、时钟除数因子为16、
看门狗定时器使能有效、两个保留位默认00、预定标器值为xxxxxxxx的内容。*/
writel( wtcon, wdt_base+ S3C2410_WTCON);
return 0;
}
/*根据标志flag的值来启动或者停止看门狗定时器,1表示启动,0表示停止*/
static void wdt_start_or_stop(int flag)
{
unsigned long wtcon;
spin_lock( & wdt_pie_lock); /*获取自旋锁保护临界区资源*/
/*停止看门狗定时器,以下各寄存器的位操作请参照数据手册*/
wtcon = readl( wdt_base+ S3C2410_WTCON);
wtcon & =~ ( S3C2410_WTCON_ENABLE| S3C2410_WTCON_RSTEN);
writel( wtcon, wdt_base+ S3C2410_WTCON);
if ( ! flag)
{
wtcon = readl( wdt_base+ S3C2410_WTCON);
wtcon | = S3C2410_WTCON_ENABLE| S3C2410_WTCON_DIV128;
if ( soft_noboot)
{
wtcon | = S3C2410_WTCON_INTEN;
wtcon & = ~ S3C2410_WTCON_RSTEN;
}
else
{
wtcon & = ~ S3C2410_WTCON_INTEN;
wtcon | = S3C2410_WTCON_RSTEN;
}
writel( wdt_count, wdt_base+ S3C2410_WTDAT);
writel( wdt_count, wdt_base+ S3C2410_WTCNT);
writel( wtcon, wdt_base+ S3C2410_WTCON);
}
spin_unlock( & wdt_pie_lock); /*释放自旋锁,即解锁*/
} de>
③ 、 实现misc设备中对设备文件的操作,代码如下:
de>#include < linux/ fs. h>
# include < linux/ interrupt. h>
# include < linux/ watchdog. h>
/*申明并初始化一个信号量open_clock,对Watchdog资源进行互斥访问,注意:这里的信号量和第②步中的
自旋锁的区别,虽然都是达到资源互斥访问的目的,但信号量是进程级的,也就是说信号量是用在多个进程
中对同一资源的互斥访问,下面的使用会在wdt_open和wdt_release两个进程中对Watchdog资源进行互斥访问。
对于自旋锁和信号量的具体区别,请在网上找,这里不再多说了*/
static DECLARE_MUTEX( open_clock);
/*用来表示Linux内核配置选项中配不配置CONFIG_WATCHDOG_NOWAYOUT项,WATCHDOG_NOWAYOUT定义在watchdog.h中*/
static int nowayout = WATCHDOG_NOWAYOUT;
typedef enum close_state
{
CLOSE_STATE_NOT,
CLOSE_STATE_ALLOW = 0x4021
} close_state_t;
static close_state_t allow_close;/*用于记录看门狗定时器的当前的操作状态*/
/*misc设备结构体实现*/
static struct miscdevice wdt_miscdev= {
. minor = WATCHDOG_MINOR,/*WATCHDOG_MINOR次设备号定义在miscdevice.h中为130*/
. name ="watchdog" , /*设备名称*/
. fops =& wdt_fops, /*实现字符设备的相关操作*/
} ;
/*字符设备的相关操作实现*/
static conststruct file_operations wdt_fops = {
. owner = THIS_MODULE,
. open = wdt_open,
. release = wdt_release,
. write = wdt_write,
. ioctl = wdt_ioctl,
. llseek = no_llseek,/*定义为不可定位,即屏蔽seek操作,no_llseek定义在fs.h中*/
} ;
/*看门狗设备驱动的打开接口函数*/
static int wdt_open(struct inode * inode,struct file* file )
{
/*试着获取信号量(即:加锁),如果获取不成功,说明其他进程此时占用了,就返回忙*/
if ( down_trylock(& open_clock))
{
return - EBUSY;
}
if ( nowayout)
{
/*如果内核配置了CONFIG_WATCHDOG_NOWAYOUT项,则使模块使用计数加1*/
__module_get( THIS_MODULE);
}
/*开始记录看门狗定时器的当前操作状态为:无状态*/
allow_close = CLOSE_STATE_NOT;
/*启动看门狗定时器*/
wdt_start_or_stop( 1);
/*表示返回的这个设备文件是不可以被seek操作的,nonseekable_open定义在fs.h中*/
return nonseekable_open( inode,file ) ;
}
/*看门狗设备驱动的关闭接口函数*/
static int wdt_release(struct inode * inode,struct file* file )
{
/*如果判断到当前操作状态是可以关闭看门狗定时器时就关闭,否则就是“喂狗”状态*/
if ( allow_close= = CLOSE_STATE_ALLOW)
{
wdt_start_or_stop( 0); /*关闭*/
}
else
{
wdt_keepalive( ) ; /*“喂狗”*/
}
/*恢复看门狗定时器的当前操作状态为:无状态*/
allow_close = CLOSE_STATE_NOT;
/*释放获取的信号量(即:解锁),与wdt_open中加锁相对应*/
up( & open_lock);
return 0;
}
/*看门狗设备驱动的写数据接口函数*/
static ssize_t wdt_write(struct file* file , const char __user* buff, size_t len, loff_t * ppos)
{
if ( len)/*判断有数据写入*/
{
if (! nowayout)/*如果没有配置内核CONFIG_WATCHDOG_NOWAYOUT选项*/
{
size_t i;
/*设看门狗定时器的当前操作状态为:无状态*/
allow_close = CLOSE_STATE_NOT;
for ( i= 0; i ! = len; i++ )
{
char c;
if ( get_user( c, data+ i) )
return - EFAULT;
if ( c = ='V' ) /*判断写入的数据是"V"时,则设看门狗定时器的当前操作状态为关闭*/
allow_close = CLOSE_STATE_ALLOW;
}
}
/*上面的意思是想要看门狗定时器可以被关闭,则内核不要配置CONFIG_WATCHDOG_NOWAYOUT选项,
对于下面这里还要“喂狗”一次,我刚开始觉得不需要,因为在看门狗定时器中断里面不断的在
“喂狗”。后来想想,这里还必须要“喂狗”一次,因为当上面我们判断到写入的数据是"V"时,
看门狗定时器的当前操作状态马上被设置为关闭,再当驱动去调用看门狗设备驱动的关闭接口函数时,
看门狗定时器中断被禁止,无法再实现“喂狗”,所以这里要手动“喂狗”一次,否则定时器溢出系统被复位*/
wdt_keepalive( ) ;
}
return len;
}
/*用于支持看门狗IO控制中获取看门狗信息的命令WDIOC_GETSUPPORT,
下面的宏和看门狗信息结构体定义在watchdog.h中*/
# define OPTIONS WDIOF_SETTIMEOUT| WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
static conststruct watchdog_info wdt_ident =
{
. options = OPTIONS,
. firmware_version = 0,
. identity = "S3C2440 Watchdog",
} ;
/*看门狗设备驱动的IO控制接口函数*/
static long wdt_ioctl(struct file* file , unsigned int cmd, unsignedlong arg )
{
void __user * argp= ( void __user * ) arg ;
int __user * p= argp;
int new_margin;
/*以下对看门狗定时器IO控制的命令定义在watchdog.h中*/
switch ( cmd)
{
case WDIOC_GETSUPPORT:/*获取看门狗的支持信息,wdt_ident定义在上面*/
return copy_to_user( argp,& wdt_ident,sizeof ( wdt_ident)) ? - EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:/*获取看门够状态*/
return put_user( 0, p);
case WDIOC_KEEPALIVE:/*喂狗命令*/
wdt_keepalive( ) ;
return 0;
case WDIOC_SETTIMEOUT:/*设置定时器溢出时间值命令(时间单位为秒)*/
if ( get_user( new_margin, p)) /*获取时间值*/
return - EFAULT;
if ( wdt_set_heartbeat( new_margin)) /*设置到计数寄存器WTCNT中*/
return - EINVAL;
wdt_keepalive( ) ; /*喂狗*/
return put_user( tmr_margin, p);
case WDIOC_GETTIMEOUT:/*读取定时器默认溢出时间值命令(时间单位为秒)*/
return put_user( tmr_margin, p);
default :
return - ENOTTY;
}
} de>
④ 、 Watchdog平台驱动的设备移除、挂起和恢复接口函数的实现,代码如下:
de>/*Watchdog平台驱动的设备移除接口函数的实现*/
static int __devexit wdt_remove(struct platform_device * dev)
{
/*释放获取的Watchdog平台设备的IO资源*/
release_resource( wdt_mem);
kfree( wdt_mem);
wdt_mem = NULL;
/*同watchdog_probe中中断的申请相对应,在那里申请中断,这里就释放中断*/
free_irq( wdt_irqno, dev);
wdt_irq = NULL;
/*释放获取的Watchdog平台设备的时钟*/
clk_disable( wdt_clock);
clk_put( wdt_clock);
wdt_clock = NULL;
/*释放Watchdog设备虚拟地址映射空间*/
iounmap( wdt_base);
/*注销misc设备*/
misc_deregister( & wdt_miscdev) ;
return 0;
}
/*Watchdog平台驱动的设备关闭接口函数的实现*/
static void wdt_shutdown(struct platform_device * dev)
{
/*停止看门狗定时器*/
wdt_start_or_stop( 0);
}
/*对Watchdog平台设备驱动电源管理的支持。CONFIG_PM这个宏定义在内核中,
当配置内核时选上电源管理,则Watchdog平台驱动的设备挂起和恢复功能均有效,
这时候你应该明白了在第②步中为什么要有device_init_wakeup(&pdev->dev, 1)这句吧!!*/
# ifdef CONFIG_PM
/*定义两个变量来分别保存挂起时的WTCON和WTDAT值,到恢复的时候使用*/
static unsignedlong wtcon_save;
static unsignedlong wtdat_save;
/*Watchdog平台驱动的设备挂起接口函数的实现*/
static int wdt_suspend(struct platform_device * dev, pm_message_t state)
{
/*保存挂起时的WTCON和WTDAT值*/
wtcon_save = readl( wdt_base+ S3C2410_WTCON);
wtdat_save = readl( wdt_base+ S3C2410_WTDAT);
/*停止看门狗定时器*/
wdt_start_or_stop( 0);
return 0;
}
/*Watchdog平台驱动的设备恢复接口函数的实现*/
static int wdt_resume(struct platform_device * dev)
{
/*恢复挂起时的WTCON和WTDAT值,注意这个顺序*/
writel( wtdat_save, wdt_base+ S3C2410_WTDAT);
writel( wtdat_save, wdt_base+ S3C2410_WTCNT);
writel( wtcon_save, wdt_base+ S3C2410_WTCON);
return 0;
}
# else /*配置内核时没选上电源管理,Watchdog平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了*/
# define wdt_suspendNULL
# define wdt_resumeNULL
# endif de>
四、回过头再来分析理解具体Watchdog驱动程序代码的结构
在上面的各步骤中,我已对Watchdog驱动程序的每行代码的作用都做了详细的讲述,但到结尾部分后,也许你会有点找不着北的感觉。的确,整个代码太长,而且用文字的方式也确实很难把整个驱动的结构描述清晰。下面,我就用图形的方式来概括上面各步骤之间的关系,使整个驱动程序的结构更加清晰明了。
- S3C2440上看门狗(Watchdog)驱动开发实例讲解
- S3C2440上看门狗(Watchdog)驱动开发实例讲解
- S3C2440上看门狗(Watchdog)驱动开发实例讲解
- 嵌入式Linux之我行——S3C2440上看门狗(Watchdog)驱动开发实例讲解
- 看门狗(Watchdog)驱动开发实例讲解
- S3C2440上触摸屏驱动实例开发讲解
- S3C2440上触摸屏驱动实例开发讲解
- S3C2440上ADC驱动实例开发讲解
- S3C2440上ADC驱动实例开发讲解
- S3C2440上触摸屏驱动实例开发讲解
- S3C2440上触摸屏驱动实例开发讲解
- S3C2440上ADC驱动实例开发讲解
- S3C2440上LCD驱动(FrameBuffer)实例开发讲解
- S3C2440上LCD驱动 (FrameBuffer)实例开发讲解
- S3C2440上LCD驱动(FrameBuffer)实例开发讲解(二)
- S3C2440上LCD驱动(FrameBuffer)实例开发讲解
- [转]S3C2440上触摸屏驱动实例开发讲解
- S3C2440上触摸屏驱动实例开发讲解(转)
- linux 常用指令集合
- 无题
- 批处理基础:if判断输入参数是否空
- 负载均衡技术的实现白皮书
- android ndk 入门实践
- S3C2440上看门狗(Watchdog)驱动开发实例讲解
- 苹果应用商店审核指南中文
- form数据栏位汇总部分说明
- glogin.sql
- 动态链接库dll,静态链接库lib, 导入库lib
- DataView
- 程序员必知8大排序3大查找(一)
- STM32经典概述
- linux 程序运行时 指定动态链接库位置 so library