RTC驱动Word下载.docx
《RTC驱动Word下载.docx》由会员分享,可在线阅读,更多相关《RTC驱动Word下载.docx(19页珍藏版)》请在冰豆网上搜索。
=THIS_MODULE,
},
};
staticint__initrtc_init(void)
/*将RTC注册成平台设备驱动*/
returnplatform_driver_register(&
rtc_driver);
}
staticvoid__exitrtc_exit(void)
/*注销RTC平台设备驱动*/
platform_driver_unregister(&
module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("
GPL"
);
MODULE_AUTHOR("
HuangGang"
MODULE_DESCRIPTION("
My2440RTCdriver"
2、RTC平台驱动结构中探测函数rtc_probe的实现。
探测就意味着在系统总线中去检测设备的存在,然后获取设备有用的相关资源信息,以便我们使用这些信息。
代码如下:
asm/irq.h>
asm/io.h>
linux/rtc.h>
linux/ioport.h>
plat/regs-rtc.h>
/*定义了一个用来保存RTC的IO端口占用的IO空间和经过虚拟映射后的内存地址*/
staticstructresource*rtc_mem;
staticvoid__iomem*rtc_base;
/*定义了两个变量来保存RTC报警中断号和TICK节拍时间中断号,NO_IRQ宏定义在irq.h中*/
staticintrtc_alarmno=NO_IRQ;
staticintrtc_tickno=NO_IRQ;
/*申明并初始化一个自旋锁rtc_pie_lock,对RTC资源进行互斥访问*/
staticDEFINE_SPINLOCK(rtc_pie_lock)
/*RTC平台驱动探测函数,注意这里为什么要使用一个__devinit,也到rtc_remove实现的地方一起讲*/
staticint__devinitrtc_probe(structplatform_device*pdev)
intret;
structrtc_device*rtc;
/*定义一个RTC设备类,rtc_device定义在rtc.h中*/
structresource*res;
/*定义一个资源,用来保存获取的RTC的资源*/
/*在系统定义的RTC平台设备中获取RTC报警中断号
platform_get_irq定义在platform_device.h中*/
rtc_alarmno=platform_get_irq(pdev,0);
if(rtc_alarmno<
0)
{
/*获取RTC报警中断号不成功错误处理
dev_err定义在device.h中,在platform_device.h中已经引用,所以这里就不需再引用了*/
dev_err(&
pdev->
dev,"
noirqforalarm\n"
return-ENOENT;
//在系统定义的RTC平台设备中获取TICK节拍时间中断号
rtc_tickno=platform_get_irq(pdev,1);
if(rtc_tickno<
/*获取TICK节拍时间中断号不成功错误处理*/
noirqforrtctick\n"
/*获取RTC平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和RTC平台设备定义中的一致*/
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(res==NULL)
/*错误处理*/
failedtogetmemoryregionresource\n"
/*申请RTC的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),
request_mem_region定义在ioport.h中*/
rtc_mem=request_mem_region(res->
start,res->
end-res->
start+1,pdev->
name);
if(rtc_mem==NULL)
failedtoreservememoryregion\n"
ret=-ENOENT;
gotoerr_nores;
/*将RTC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
注意:
IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/
rtc_base=ioremap(res->
start+1);
if(rtc_base==NULL)
failedioremap()\n"
ret=-EINVAL;
gotoerr_nomap;
/*好了,通过上面的步骤已经将RTC的资源都准备好了,下面就开始使用啦*/
/*这两个函数开始对RTC寄存器操作,定义都在下面*/
rtc_enable(pdev,1);
/*对RTC的实时时钟控制寄存器RTCCON进行操作(功能是初始化或者使能RTC)*/
rtc_setfreq(&
dev,1);
/*对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:
节拍时间计数值的设定*
/*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:
staticinlinevoiddevice_init_wakeup(structdevice*dev,intval){
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(&
/*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的,
注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第③步中。
rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/
rtc=rtc_device_register("
my2440"
&
dev,&
rtcops,THIS_MODULE);
if(IS_ERR(rtc))
cannotattachrtc\n"
ret=PTR_ERR(rtc);
gotoerr_nortc;
/*设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值,
这里你可能不理解这句,没关系,等你看到rtc_setfreq函数实现后自然就明白了*/
rtc->
max_user_freq=128;
/*将RTC设备类的数据传递给系统平台设备。
platform_set_drvdata是定义在platform_device.h的宏,如下:
#defineplatform_set_drvdata(_dev,data)
dev_set_drvdata(&
(_dev)->
dev,(data))
而dev_set_drvdata又被定义在include/linux/device.h中,如下:
staticinlinevoiddev_set_drvdata(structdevice*dev,void*data){
driver_data=data;
}*/
platform_set_drvdata(pdev,rtc);
return0;
//以下是上面错误处理的跳转点
err_nortc:
rtc_enable(pdev,0);
iounmap(rtc_base);
err_nomap:
release_resource(rtc_mem);
err_nores:
returnret;
/*该函数主要是初始化或者使能RTC,
以下RTC的各种寄存器的宏定义在arch/arm/plat-s3c/include/plat/regs-rtc.h中,
各寄存器的用途和设置请参考S3C2440数据手册的第十七章实时时钟部分*/
staticvoidrtc_enable(structplatform_device*pdev,intflag)
unsignedinttmp;
/*RTC的实时时钟控制寄存器RTCCON共有4个位,各位的初始值均为0,根据数据手册介绍第0位(即:
RCTEN位)
可以控制CPU和RTC之间的所有接口(即RTC使能功能),所以在系统复位后应该将RTCCON寄存器的第0为置为1;
在关闭电源前,又应该将该位清零,以避免无意的写RTC寄存器*/
if(!
flag)
/*当flag=0时(即属于关闭电源前的情况),RTCCON寄存器清零第一位*/
tmp=readb(rtc_base+S3C2410_RTCCON);
/*读取RTCCON寄存器的值*/
/*tmp&
~S3C2410_RTCCON_RTCEN=0即屏蔽RTC使能*/
writeb(tmp&
~S3C2410_RTCCON_RTCEN,rtc_base+S3C2410_RTCCON);
tmp=readb(rtc_base+S3C2410_TICNT);
/*读取TICNT寄存器的值*/
~S3C2410_TICNT_ENABLE后第7位为0,即屏蔽节拍时间中断使能*/
~S3C2410_TICNT_ENABLE,rtc_base+S3C2410_TICNT);
}
else
/*当flag!
=0时(即属于系统复位后的情况),使能RTC*/
if((readb(rtc_base+S3C2410_RTCCON)&
S3C2410_RTCCON_RTCEN)==0)
dev_info(&
rtcdisabled,re-enabling\n"
writeb(tmp|S3C2410_RTCCON_RTCEN,rtc_base+S3C2410_RTCCON);
S3C2410_RTCCON_CNTSEL))
removingRTCCON_CNTSEL\n"
~S3C2410_RTCCON_CNTSEL,rtc_base+S3C2410_RTCCON);
S3C2410_RTCCON_CLKRST))
removingRTCCON_CLKRST\n"
~S3C2410_RTCCON_CLKRST,rtc_base+S3C2410_RTCCON);
/*该函数主要是对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:
节拍时间计数值的设定*/
staticintrtc_setfreq(structdevice*dev,intfreq)
is_power_of_2(freq))/*对freq的值进行检查*/
return-EINVAL;
spin_lock_irq(&
rtc_pie_lock);
/*获取自旋锁保护临界区资源*/
/*读取节拍时间计数寄存器TICNT的值*/
tmp=readb(rtc_base+S3C2410_TICNT)&
S3C2410_TICNT_ENABLE;
/*看数据手册得知,节拍时间计数值的范围是1-127,
还记得在rtc_enable函数中设置的rtc->
max_user_freq=128吗?
所以这里要减1*/
tmp|=(128/freq)-1;
*将经运算后值写入节拍时间计数寄存器TICNT中,这里主要是改变TICNT的第0-6位的值*/
writeb(tmp,rtc_base+S3C2410_TICNT);
spin_unlock_irq(&
/*释放自旋锁,即解锁*/
3、RTC设备类的操作。
在这一步中,才是对RTC硬件的各种寄存器进行操作,代码如下:
linux/interrupt.h>
linux/bcd.h>
/*rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,
类似字符设备在驱动中的file_operations对字符设备进行操作的意思。
该结构体被定义
在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,
该结构体内接口函数的实现都在下面*/
staticconststructrtc_class_opsrtcops={
.open
=rtc_open,
.release
=rtc_release,
.irq_set_freq
=rtc_setfreq,/*在第②步中已实现*/
.irq_set_state
=rtc_setpie,
.read_time
=rtc_gettime,
.set_time
=rtc_settime,
.read_alarm
=rtc_getalarm,
.set_alarm
=rtc_setalarm,
/*RTC设备类打开接口函数*/
staticintrtc_open(structdevice*dev)
/*这里主要的目的是从系统平台设备中获取RTC设备类的数据,和RTC探测函数rtc_probe中
的platform_set_drvdata(pdev,rtc)的操作刚好相反。
这些都定义在platform_device.h中*/
structplatform_device*pdev=to_platform_device(dev);
structrtc_device*rtc_dev=platform_get_drvdata(pdev);
/*申请RTC报警中断服务,中断号rtc_alarmno在RTC探测函数rtc_probe中已经获取得,
这里使用的是快速中断:
IRQF_DISABLED。
中断服务程序为:
rtc_alarmirq,将RTC设备类rtc_dev做参数传递过去了*/
ret=request_irq(rtc_alarmno,rtc_alarmirq,IRQF_DISABLED,"
my2440-rtcalarm"
rtc_dev);
if(ret)
dev_err(dev,"
IRQ%derror%d\n"
rtc_alarmno,ret);
/*同上面一样,这里申请的是RTC的TICK节拍时间中断服务,服务程序是:
rtc_tickirq*/
ret=request_irq(rtc_tickno,rtc_tickirq,IRQF_DISABLED,"
my2440-rtctick"
rtc_tickno,ret);
gototick_err;
tick_err:
/*错误处理,注意出现错误后也要释放掉已经申请成功的中断*/
free_irq(rtc_alarmno,rtc_dev);
/*RTC报警中断服务程序*/
staticirqreturn_trtc_alarmirq(intirq,void*argv)
structrtc_device*rdev=argv;
/*接收申请中断时传递过来的rtc_dev参数*/
/*当报警中断到来的时候,去设定RTC中报警的相关信息,具体设定的方法,RTC核心
部分已经在rtc_update_irq接口函数中实现,函数定义实现在interface.c中*/
rtc_update_irq(rdev,1,RTC_AF|RTC_IRQF);
returnIRQ_HANDLED;
/*RTC的TICK节拍时间中断服务*/
staticirqreturn_trtc_tickirq(intirq,void*argv)
/*节拍时间中断到来的时候,去设定RTC中节拍时间的相关信息,具体设定的方法,RTC核心
rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
returnIRQ_HA