RTC驱动.docx
- 文档编号:9301603
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:19
- 大小:25.56KB
RTC驱动.docx
《RTC驱动.docx》由会员分享,可在线阅读,更多相关《RTC驱动.docx(19页珍藏版)》请在冰豆网上搜索。
RTC驱动
1、依然是驱动程序的最基本结构:
RTC驱动的初始化和退出部分及其他,如下:
#include
#include
#include
#include
#include
/*RTC平台驱动结构体,平台驱动结构体定义在platform_device.h中,该结构体内的接口函数在第②、④步中实现*/
staticstructplatform_driverrtc_driver=
{
.probe =rtc_probe,/*RTC探测函数,在第②步中实现*/
.remove =__devexit_p(rtc_remove),/*RTC移除函数,在第④步实现,为何使用__devexit_p,在该函数实现的地方再讲*/
.suspend=rtc_suspend,/*RTC挂起函数,在第④步中实现*/
.resume =rtc_resume,/*RTC恢复函数,在第④步中实现*/
.driver =
{
/*注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来*/
.name ="s3c2410-rtc",
.owner =THIS_MODULE,
},
};
staticint__initrtc_init(void)
{
/*将RTC注册成平台设备驱动*/
returnplatform_driver_register(&rtc_driver);
}
staticvoid__exitrtc_exit(void)
{
/*注销RTC平台设备驱动*/
platform_driver_unregister(&rtc_driver);
}
module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HuangGang");
MODULE_DESCRIPTION("My2440RTCdriver");
2、RTC平台驱动结构中探测函数rtc_probe的实现。
探测就意味着在系统总线中去检测设备的存在,然后获取设备有用的相关资源信息,以便我们使用这些信息。
代码如下:
#include
#include
#include
#include
#include
/*定义了一个用来保存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<0)
{
/*获取TICK节拍时间中断号不成功错误处理*/
dev_err(&pdev->dev,"noirqforrtctick\n");
return-ENOENT;
}
/*获取RTC平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和RTC平台设备定义中的一致*/
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(res==NULL)
{
/*错误处理*/
dev_err(&pdev->dev,"failedtogetmemoryregionresource\n");
return-ENOENT;
}
/*申请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)
{
/*错误处理*/
dev_err(&pdev->dev,"failedtoreservememoryregion\n");
ret=-ENOENT;
gotoerr_nores;
}
/*将RTC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
注意:
IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/
rtc_base=ioremap(res->start,res->end-res->start+1);
if(rtc_base==NULL)
{
/*错误处理*/
dev_err(&pdev->dev,"failedioremap()\n");
ret=-EINVAL;
gotoerr_nomap;
}
/*好了,通过上面的步骤已经将RTC的资源都准备好了,下面就开始使用啦*/
/*这两个函数开始对RTC寄存器操作,定义都在下面*/
rtc_enable(pdev,1);/*对RTC的实时时钟控制寄存器RTCCON进行操作(功能是初始化或者使能RTC)*/
rtc_setfreq(&pdev->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(&pdev->dev,1);
/*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的,
注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第③步中。
rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/
rtc=rtc_device_register("my2440",&pdev->dev,&rtcops,THIS_MODULE);
if(IS_ERR(rtc))
{
/*错误处理*/
dev_err(&pdev->dev,"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){
dev->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寄存器的值*/
/*tmp&~S3C2410_TICNT_ENABLE后第7位为0,即屏蔽节拍时间中断使能*/
writeb(tmp&~S3C2410_TICNT_ENABLE,rtc_base+S3C2410_TICNT);
}
else
{
/*当flag!
=0时(即属于系统复位后的情况),使能RTC*/
if((readb(rtc_base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0)
{
dev_info(&pdev->dev,"rtcdisabled,re-enabling\n");
tmp=readb(rtc_base+S3C2410_RTCCON);
writeb(tmp|S3C2410_RTCCON_RTCEN,rtc_base+S3C2410_RTCCON);
}
if((readb(rtc_base+S3C2410_RTCCON)&S3C2410_RTCCON_CNTSEL))
{
dev_info(&pdev->dev,"removingRTCCON_CNTSEL\n");
tmp=readb(rtc_base+S3C2410_RTCCON);
writeb(tmp&~S3C2410_RTCCON_CNTSEL,rtc_base+S3C2410_RTCCON);
}
if((readb(rtc_base+S3C2410_RTCCON)&S3C2410_RTCCON_CLKRST))
{
dev_info(&pdev->dev,"removingRTCCON_CLKRST\n");
tmp=readb(rtc_base+S3C2410_RTCCON);
writeb(tmp&~S3C2410_RTCCON_CLKRST,rtc_base+S3C2410_RTCCON);
}
}
}
/*该函数主要是对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:
节拍时间计数值的设定*/
staticintrtc_setfreq(structdevice*dev,intfreq)
{
unsignedinttmp;
if(!
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(&rtc_pie_lock);/*释放自旋锁,即解锁*/
return0;
}
3、RTC设备类的操作。
在这一步中,才是对RTC硬件的各种寄存器进行操作,代码如下:
#include
#include
/*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)
{
intret;
/*这里主要的目的是从系统平台设备中获取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);
returnret;
}
/*同上面一样,这里申请的是RTC的TICK节拍时间中断服务,服务程序是:
rtc_tickirq*/
ret=request_irq(rtc_tickno,rtc_tickirq,IRQF_DISABLED,"my2440-rtctick",rtc_dev);
if(ret)
{
dev_err(dev,"IRQ%derror%d\n",rtc_tickno,ret);
gototick_err;
}
returnret;
tick_err:
/*错误处理,注意出现错误后也要释放掉已经申请成功的中断*/
free_irq(rtc_alarmno,rtc_dev);
returnret;
}
/*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)
{
structrtc_device*rdev=argv;/*接收申请中断时传递过来的rtc_dev参数*/
/*节拍时间中断到来的时候,去设定RTC中节拍时间的相关信息,具体设定的方法,RTC核心
部分已经在rtc_update_irq接口函数中实现,函数定义实现在interface.c中*/
rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
returnIRQ_HA
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- RTC 驱动