新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > ARM-Linux驱动--RTC(实时时钟)驱动分析

ARM-Linux驱动--RTC(实时时钟)驱动分析

作者: 时间:2016-11-20 来源:网络 收藏
硬件平台:FL2440(S3C2440)

内核版本:Linux 2.6.28

本文引用地址:https://www.eepw.com.cn/article/201611/318867.htm

主机平台:Ubuntu 11.04

内核版本:Linux 2.6.39

交叉编译器版本:arm-linux-gcc 3.4.1

原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6584285

1、实时时钟概述

实时时钟(RTC)单元可以在断电的情况下使用纽扣电池继续计时工作。RTC使用STRB/LDRB ARM操作传输二进制码十进制数的8位数据给CPU。其中的数据包括秒、分、时、日期、天、月、年的时间信息。可以执行报警功能。

2、实时时钟操作

下面是RTC模块的电路图

3、RTC寄存器介绍

实时时钟控制寄存器(RTCCON)-REAL TIME CLOCK CONTROL REGISTER

节拍时间计数寄存器(TICNT)-TICK TIME COUNT REGISTER

RTC报警控制寄存器(RTCALM)-RTC ALARM CONTROL REGISTER

报警秒数寄存器(ALMSEC)-ALARM SECOND DATA REGISTER

报警分钟计数寄存器(ALMMIN)-ALARM MIN DATA REGISTER

报警小时数据寄存器(ALMHOUR)-ALARM HOUR DATA REGISTER

报警日期数据寄存器(ALMDATE)-ALARM DATE DATA REGISTER

报警月数数据寄存器(ALMMON)-ALARM MON DATA REGISTER

报警年数数据寄存器(ALMYEAR)-ALARM YEAR DATA REGISTER

BCD数据寄存器的格式和报警寄存器结构相同,只是对应的地址不同。

BCD秒寄存器(BCDSEC)-BCD SECOND REGISTER 地址:0x57000070(L)0x57000073(B)

BCD分寄存器(BCDMIN)-BCD MINUTE REGISTER 地址:0x57000074(L)0x57000077(B)

BCD小时寄存器(BCDHOUR)-BCD HOUR REGISTER 地址:0x57000078(L)0x5700007B(B)

BCD日期寄存器(BCDDATE)-BCD DATE REGISTER 地址:0x5700007C(L)0x5700007F(B)

BCD日寄存器(BCDDAY)-BCD DAY REGISTER 地址:0x57000080(L)0x57000083(B)

BCD月寄存器(BCDMON)-BCD MONTH REGISTER 地址:0x57000084(L)0x57000087(B)

BCD年寄存器(BCDYEAR)-BCD YEAR REGISTER 地址:0x57000088(L)0x5700008B(B)

4、驱动实例分析

为了使驱动更容易理解,现在这个RTC驱动只完成了计时功能,没有添加相应的报警功能,也没有添加电源管理的功能,缺少的功能今后完善。

下面先总体了解驱动:

首先是RTC驱动的结构体,在/include/linux/platform_device.h中,如下

[cpp]view plaincopy
  1. structplatform_driver{
  2. int(*probe)(structplatform_device*);
  3. int(*remove)(structplatform_device*);
  4. void(*shutdown)(structplatform_device*);
  5. int(*suspend)(structplatform_device*,pm_message_tstate);
  6. int(*suspend_late)(structplatform_device*,pm_message_tstate);
  7. int(*resume_early)(structplatform_device*);
  8. int(*resume)(structplatform_device*);
  9. structpm_ext_ops*pm;
  10. structdevice_driverdriver;
  11. };
驱动中定义对应的结构体

[cpp]view plaincopy
  1. staticstructplatform_drivers3c2410_rtc_driver={
  2. .probe=s3c_rtc_probe,//RTC探测函数
  3. .remove=__devexit_p(s3c_rtc_remove),//RTC移除函数
  4. .driver={
  5. .name="s3c2410-rtc",
  6. .owner=THIS_MODULE,
  7. },
  8. };
下面是驱动中驱动的初始化和退出函数

[cpp]view plaincopy
  1. staticint__inits3c_rtc_init(void)
  2. {
  3. printk(banner);
  4. returnplatform_driver_register(&s3c2410_rtc_driver);
  5. }
  6. staticvoid__exits3c_rtc_exit(void)
  7. {
  8. platform_driver_unregister(&s3c2410_rtc_driver);
  9. }

platform_driver_register()和platform_driver_unregister()函数在/drivers/base/platform.c中实现的。

可以看出,platform_driver_register()函数的作用就是为platform_driver中的driver中的probe、remove等提供接口函数

[cpp]view plaincopy
  1. intplatform_driver_register(structplatform_driver*drv)
  2. {
  3. drv->driver.bus=&platform_bus_type;
  4. if(drv->probe)
  5. drv->driver.probe=platform_drv_probe;
  6. if(drv->remove)
  7. drv->driver.remove=platform_drv_remove;
  8. if(drv->shutdown)
  9. drv->driver.shutdown=platform_drv_shutdown;
  10. if(drv->suspend)
  11. drv->driver.suspend=platform_drv_suspend;
  12. if(drv->resume)
  13. drv->driver.resume=platform_drv_resume;
  14. if(drv->pm)
  15. drv->driver.pm=&drv->pm->base;
  16. returndriver_register(&drv->driver);//注册老的驱动
  17. }
[cpp]view plaincopy
  1. voidplatform_driver_unregister(structplatform_driver*drv)
  2. {
  3. driver_unregister(&drv->driver);
  4. }

接下来是RTC平台驱动探测函数s3c_rtc_probe,下面函数定义的时候使用了__devinit的作用是使编译器优化代码,将其放在和是的内存位置,减少内存占用和提高内核效率。

probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。

[cpp]view plaincopy
  1. staticint__devinits3c_rtc_probe(structplatform_device*pdev)
  2. {
  3. structrtc_device*rtc;//定义rtc_device结构体,定义在/include/linux/rtc.h
  4. structresource*res;//定义资源结构体,定义在/include/linux/ioport.h
  5. intret;
  6. pr_debug("%s:probe=%pn",__func__,pdev);
  7. /*findtheIRQs*/
  8. s3c_rtc_tickno=platform_get_irq(pdev,1);//在系统定义的平台设备中获取中断号
  9. if(s3c_rtc_tickno<0){//异常处理
  10. dev_err(&pdev->dev,"noirqforrtctickn");
  11. return-ENOENT;
  12. }
  13. /*getthememoryregion*/
  14. res=platform_get_resource(pdev,IORESOURCE_MEM,0);//获取RTC平台使用的IO资源
  15. if(res==NULL){
  16. dev_err(&pdev->dev,"failedtogetmemoryregionresourcen");
  17. return-ENOENT;
  18. }
  19. //申请内存区域,res是structresource类型,见本函数后面
  20. s3c_rtc_mem=request_mem_region(res->start,
  21. res->end-res->start+1,
  22. pdev->name);
  23. if(s3c_rtc_mem==NULL){//申请内存出错
  24. dev_err(&pdev->dev,"failedtoreservememoryregionn");
  25. ret=-ENOENT;
  26. gotoerr_nores;
  27. }
  28. //将寄存器地址映射成虚拟地址,以便访问
  29. s3c_rtc_base=ioremap(res->start,res->end-res->start+1);
  30. if(s3c_rtc_base==NULL){
  31. dev_err(&pdev->dev,"failedioremap()n");
  32. ret=-EINVAL;
  33. gotoerr_nomap;
  34. }
  35. /*checktoseeifeverythingissetupcorrectly*/
  36. s3c_rtc_enable(pdev,1);//对RTCCON寄存器设置,详情见下面的函数实现
  37. pr_debug("s3c2410_rtc:RTCCON=%02xn",
  38. readb(s3c_rtc_base+S3C2410_RTCCON));
  39. s3c_rtc_setfreq(&pdev->dev,1);//详情见下面的函数实现
  40. /*registerRTCandexit*/
  41. rtc=rtc_device_register("s3c",&pdev->dev,&s3c_rtcops,
  42. THIS_MODULE);//注册RTC为RTC设备,其中s3c_rtcops定义见下
  43. if(IS_ERR(rtc)){
  44. dev_err(&pdev->dev,"cannotattachrtcn");
  45. ret=PTR_ERR(rtc);
  46. gotoerr_nortc;
  47. }
  48. rtc->max_user_freq=128;//设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值
  49. //将RTC类的设备数据传递给系统设备,在/include/linux/platform_device.h中
[cpp]view plaincopy
  1. //#defineplatform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)->dev,(data)),该函数在/include/linux/device.h中定义,见本函数下面
[cpp]view plaincopy
  1. platform_set_drvdata(pdev,rtc);
[cpp]view plaincopy
  1. return0;
[cpp]view plaincopy
  1. //异常处理
  2. err_nortc:
  3. s3c_rtc_enable(pdev,0);
  4. iounmap(s3c_rtc_base);
  5. err_nomap:
  6. release_resource(s3c_rtc_mem);
  7. err_nores:
  8. returnret;
  9. }
下面是/include/linux/ioport.h中struct resource结构体定义
[cpp]view plaincopy
  1. structresource{
  2. resource_size_tstart;
  3. resource_size_tend;
  4. constchar*name;
  5. unsignedlongflags;
  6. structresource*parent,*sibling,*child;
  7. };
这是dev_set_drvdata()的函数定义:

[cpp]view plaincopy
  1. staticinlinevoiddev_set_drvdata(structdevice*dev,void*data)
  2. {
  3. dev->driver_data=data;
  4. }
接下来是在s3c_rtc_probe()函数用到的两个函数s3c_rtc_enable()和s3c_rtc_setfreq()

  1. staticvoids3c_rtc_enable(structplatform_device*pdev,inten)
  2. {
  1. void__iomem*base=s3c_rtc_base;//__iomem的作用就是为了使编译器更好的优化编译
  2. unsignedinttmp;
  3. if(s3c_rtc_base==NULL)
  4. return;
  5. //en作为参数传递过来如果en==0,关闭电源前的情况
  6. if(!en){
  7. tmp=readb(base+S3C2410_RTCCON);
  1. writeb(tmp&~S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);//设置RTCCON寄存器,屏蔽RTC使能,可以参考数据手册中寄存器的相关定义
  2. tmp=readb(base+S3C2410_TICNT);
  3. writeb(tmp&~S3C2410_TICNT_ENABLE,base+S3C2410_TICNT);//设置TICNT寄存器,屏蔽节拍时间中断使能
  4. }else{
  5. /*re-enablethedevice,andcheckitisok*/
  6. //en!=0的情况,表示系统复位,重新使能RTC驱动
  7. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0){//RTCCON第0位为0,将其设置为1,重新使能
  8. dev_info(&pdev->dev,"rtcdisabled,re-enablingn");
  9. tmp=readb(base+S3C2410_RTCCON);
  10. writeb(tmp|S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
  11. }
  12. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CNTSEL)){
  13. dev_info(&pdev->dev,"removingRTCCON_CNTSELn");
  14. tmp=readb(base+S3C2410_RTCCON);
  15. writeb(tmp&~S3C2410_RTCCON_CNTSEL,base+S3C2410_RTCCON);//设置RTCCON第2位为0,设置BCD计数为混合BCD计数
  16. }
  17. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CLKRST)){
  18. dev_info(&pdev->dev,"removingRTCCON_CLKRSTn");
  19. tmp=readb(base+S3C2410_RTCCON);
  20. writeb(tmp&~S3C2410_RTCCON_CLKRST,base+S3C2410_RTCCON);//RTC时钟计数器复位
  21. }
  22. }
  23. }
[cpp]view plaincopy
  1. staticints3c_rtc_setfreq(structdevice*dev,intfreq)//设定节拍时间计数值
  2. {
  3. unsignedinttmp;
  4. spin_lock_irq(&s3c_rtc_pie_lock);//获取自旋锁,对资源互斥访问
  5. tmp=readb(s3c_rtc_base+S3C2410_TICNT)&S3C2410_TICNT_ENABLE;//节拍时间使能有效
  6. tmp|=(128/freq)-1;
  7. writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
  8. spin_unlock_irq(&s3c_rtc_pie_lock);//解锁
  9. return0;
  10. }
接下来是RTC设备类的操作。

下面是rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。该结构体被定义在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结构体内接口函数的实现都在下面

  1. staticconststructrtc_class_opss3c_rtcops={
  2. .open=s3c_rtc_open,
  3. .release=s3c_rtc_release,
  4. .read_time=s3c_rtc_gettime,
  5. .set_time=s3c_rtc_settime,
  6. .irq_set_freq=s3c_rtc_setfreq,
  7. .irq_set_state=s3c_rtc_setpie,
  8. };
RTC打开设备函数s3c_rtc_open()
  1. staticints3c_rtc_open(structdevice*dev)
  2. {
  3. structplatform_device*pdev=to_platform_device(dev);//从平台设备中获取RTC设备类的数据
  4. structrtc_device*rtc_dev=platform_get_drvdata(pdev);
  5. intret;
  6. ret=request_irq(s3c_rtc_tickno,s3c_rtc_tickirq,
  7. IRQF_DISABLED,"s3c2410-rtctick",rtc_dev);//申请中断
  8. if(ret){
  9. dev_err(dev,"IRQ%derror%dn",s3c_rtc_tickno,ret);
  10. gototick_err;
  11. }
  12. tick_err:
  13. returnret;
  14. }
RTC TICK节拍时间中断服务程序
  1. staticirqreturn_ts3c_rtc_tickirq(intirq,void*id)
  2. {
  3. structrtc_device*rdev=id;
  4. rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
  5. returnIRQ_HANDLED;
  6. }
RTC关闭设备函数s3c_rtc_release()

  1. staticvoids3c_rtc_release(structdevice*dev)
  2. {
  3. structplatform_device*pdev=to_platform_device(dev);//从平台设备中获取RTC设备类的数据
  4. structrtc_device*rtc_dev=platform_get_drvdata(pdev);
  5. /*donotclearAIEhere,itmaybeneededforwake*/
  6. s3c_rtc_setpie(dev,0);//函数定义见下面
  7. free_irq(s3c_rtc_tickno,rtc_dev);
  8. }
s3c_rtc_setpie()函数,该函数主要作用就是根据参数设置TICNT寄存器的最高位,参数为0,禁止使能,参数为1,使能
  1. staticints3c_rtc_setpie(structdevice*dev,intenabled)
  2. {
  3. unsignedinttmp;
  4. pr_debug("%s:pie=%dn",__func__,enabled);
  5. spin_lock_irq(&s3c_rtc_pie_lock);
  6. tmp=readb(s3c_rtc_base+S3C2410_TICNT)&~S3C2410_TICNT_ENABLE;//读取TICNT的值并将最高位清0
  7. if(enabled)
  8. tmp|=S3C2410_TICNT_ENABLE;
  9. writeb(tmp,s3c_rtc_base+S3C2410_TICNT);//写入计算后新的值
  10. spin_unlock_irq(&s3c_rtc_pie_lock);
  11. return0;
  12. }
下面两个函数是设置和读取BCD寄存器的时间,逻辑很简单,只是读取和设置相应寄存器的值

  1. staticints3c_rtc_gettime(structdevice*dev,structrtc_time*rtc_tm)
  2. {
  3. unsignedinthave_retried=0;
  4. void__iomem*base=s3c_rtc_base;
  5. retry_get_time:
  6. rtc_tm->tm_min=readb(base+S3C2410_RTCMIN);
  7. rtc_tm->tm_hour=readb(base+S3C2410_RTCHOUR);
  8. rtc_tm->tm_mday=readb(base+S3C2410_RTCDATE);
  9. rtc_tm->tm_mon=readb(base+S3C2410_RTCMON);
  10. rtc_tm->tm_year=readb(base+S3C2410_RTCYEAR);
  11. rtc_tm->tm_sec=readb(base+S3C2410_RTCSEC);
  12. /*theonlywaytoworkoutwetherthesystemwasmid-update
  13. *whenwereaditistocheckthesecondcounter,andifit
  14. *iszero,thenwere-trytheentireread
  15. */
  16. if(rtc_tm->tm_sec==0&&!have_retried){
  17. have_retried=1;
  18. gotoretry_get_time;
  19. }
  20. pr_debug("readtime%02x.%02x.%02x%02x/%02x/%02xn",
  21. rtc_tm->tm_year,rtc_tm->tm_mon,rtc_tm->tm_mday,
  22. rtc_tm->tm_hour,rtc_tm->tm_min,rtc_tm->tm_sec);
  23. rtc_tm->tm_sec=bcd2bin(rtc_tm->tm_sec);
  24. rtc_tm->tm_min=bcd2bin(rtc_tm->tm_min);
  25. rtc_tm->tm_hour=bcd2bin(rtc_tm->tm_hour);
  26. rtc_tm->tm_mday=bcd2bin(rtc_tm->tm_mday);
  27. rtc_tm->tm_mon=bcd2bin(rtc_tm->tm_mon);
  28. rtc_tm->tm_year=bcd2bin(rtc_tm->tm_year);
  29. rtc_tm->tm_year+=100;
  30. rtc_tm->tm_mon-=1;
  31. return0;
  32. }
  33. staticints3c_rtc_settime(structdevice*dev,structrtc_time*tm)
  34. {
  35. void__iomem*base=s3c_rtc_base;
  36. intyear=tm->tm_year-100;
  37. pr_debug("settime%02d.%02d.%02d%02d/%02d/%02dn",
  38. tm->tm_year,tm->tm_mon,tm->tm_mday,
  39. tm->tm_hour,tm->tm_min,tm->tm_sec);
  40. /*wegetaroundy2kbysimplynotsupportingit*/
  41. if(year<0||year>=100){
  42. dev_err(dev,"rtconlysupports100yearsn");
  43. return-EINVAL;
  44. }
  45. writeb(bin2bcd(tm->tm_sec),base+S3C2410_RTCSEC);
  46. writeb(bin2bcd(tm->tm_min),base+S3C2410_RTCMIN);
  47. writeb(bin2bcd(tm->tm_hour),base+S3C2410_RTCHOUR);
  48. writeb(bin2bcd(tm->tm_mday),base+S3C2410_RTCDATE);
  49. writeb(bin2bcd(tm->tm_mon+1),base+S3C2410_RTCMON);
  50. writeb(bin2bcd(year),base+S3C2410_RTCYEAR);
  51. return0;
  52. }
到这里RTC驱动的计时功能实现,报警功能还没有完成。下面是这个驱动源代码

  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #includeinterrupt.h>
  7. #include
  8. #include
  9. #include
  10. #include
  11. #include
  12. #include
  13. #include
  14. #include
  15. #include
  16. staticstructresource*s3c_rtc_mem;
  17. staticvoid__iomem*s3c_rtc_base;
  18. staticints3c_rtc_tickno=NO_IRQ;
  19. staticDEFINE_SPINLOCK(s3c_rtc_pie_lock);
  20. staticirqreturn_ts3c_rtc_tickirq(intirq,void*id)
  21. {
  22. structrtc_device*rdev=id;
  23. rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
  24. returnIRQ_HANDLED;
  25. }
  26. /*Updatecontrolregisters*/
  27. staticvoids3c_rtc_setaie(intto)
  28. {
  29. unsignedinttmp;
  30. pr_debug("%s:aie=%dn",__func__,to);
  31. tmp=readb(s3c_rtc_base+S3C2410_RTCALM)&~S3C2410_RTCALM_ALMEN;
  32. if(to)
  33. tmp|=S3C2410_RTCALM_ALMEN;
  34. writeb(tmp,s3c_rtc_base+S3C2410_RTCALM);
  35. }
  36. staticints3c_rtc_setpie(structdevice*dev,intenabled)
  37. {
  38. unsignedinttmp;
  39. pr_debug("%s:pie=%dn",__func__,enabled);
  40. spin_lock_irq(&s3c_rtc_pie_lock);
  41. tmp=readb(s3c_rtc_base+S3C2410_TICNT)&~S3C2410_TICNT_ENABLE;
  42. if(enabled)
  43. tmp|=S3C2410_TICNT_ENABLE;
  44. writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
  45. spin_unlock_irq(&s3c_rtc_pie_lock);
  46. return0;
  47. }
  48. staticints3c_rtc_setfreq(structdevice*dev,intfreq)
  49. {
  50. unsignedinttmp;
  51. spin_lock_irq(&s3c_rtc_pie_lock);
  52. tmp=readb(s3c_rtc_base+S3C2410_TICNT)&S3C2410_TICNT_ENABLE;
  53. tmp|=(128/freq)-1;
  54. writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
  55. spin_unlock_irq(&s3c_rtc_pie_lock);
  56. return0;
  57. }
  58. /*Timeread/write*/
  59. staticints3c_rtc_gettime(structdevice*dev,structrtc_time*rtc_tm)
  60. {
  61. unsignedinthave_retried=0;
  62. void__iomem*base=s3c_rtc_base;
  63. retry_get_time:
  64. rtc_tm->tm_min=readb(base+S3C2410_RTCMIN);
  65. rtc_tm->tm_hour=readb(base+S3C2410_RTCHOUR);
  66. rtc_tm->tm_mday=readb(base+S3C2410_RTCDATE);
  67. rtc_tm->tm_mon=readb(base+S3C2410_RTCMON);
  68. rtc_tm->tm_year=readb(base+S3C2410_RTCYEAR);
  69. rtc_tm->tm_sec=readb(base+S3C2410_RTCSEC);
  70. /*theonlywaytoworkoutwetherthesystemwasmid-update
  71. *whenwereaditistocheckthesecondcounter,andifit
  72. *iszero,thenwere-trytheentireread
  73. */
  74. if(rtc_tm->tm_sec==0&&!have_retried){
  75. have_retried=1;
  76. gotoretry_get_time;
  77. }
  78. pr_debug("readtime%02x.%02x.%02x%02x/%02x/%02xn",
  79. rtc_tm->tm_year,rtc_tm->tm_mon,rtc_tm->tm_mday,
  80. rtc_tm->tm_hour,rtc_tm->tm_min,rtc_tm->tm_sec);
  81. rtc_tm->tm_sec=bcd2bin(rtc_tm->tm_sec);
  82. rtc_tm->tm_min=bcd2bin(rtc_tm->tm_min);
  83. rtc_tm->tm_hour=bcd2bin(rtc_tm->tm_hour);
  84. rtc_tm->tm_mday=bcd2bin(rtc_tm->tm_mday);
  85. rtc_tm->tm_mon=bcd2bin(rtc_tm->tm_mon);
  86. rtc_tm->tm_year=bcd2bin(rtc_tm->tm_year);
  87. rtc_tm->tm_year+=100;
  88. rtc_tm->tm_mon-=1;
  89. return0;
  90. }
  91. staticints3c_rtc_settime(structdevice*dev,structrtc_time*tm)
  92. {
  93. void__iomem*base=s3c_rtc_base;
  94. intyear=tm->tm_year-100;
  95. pr_debug("settime%02d.%02d.%02d%02d/%02d/%02dn",
  96. tm->tm_year,tm->tm_mon,tm->tm_mday,
  97. tm->tm_hour,tm->tm_min,tm->tm_sec);
  98. /*wegetaroundy2kbysimplynotsupportingit*/
  99. if(year<0||year>=100){
  100. dev_err(dev,"rtconlysupports100yearsn");
  101. return-EINVAL;
  102. }
  103. writeb(bin2bcd(tm->tm_sec),base+S3C2410_RTCSEC);
  104. writeb(bin2bcd(tm->tm_min),base+S3C2410_RTCMIN);
  105. writeb(bin2bcd(tm->tm_hour),base+S3C2410_RTCHOUR);
  106. writeb(bin2bcd(tm->tm_mday),base+S3C2410_RTCDATE);
  107. writeb(bin2bcd(tm->tm_mon+1),base+S3C2410_RTCMON);
  108. writeb(bin2bcd(year),base+S3C2410_RTCYEAR);
  109. return0;
  110. }
  111. staticints3c_rtc_open(structdevice*dev)
  112. {
  113. structplatform_device*pdev=to_platform_device(dev);
  114. structrtc_device*rtc_dev=platform_get_drvdata(pdev);
  115. intret;
  116. ret=request_irq(s3c_rtc_tickno,s3c_rtc_tickirq,
  117. IRQF_DISABLED,"s3c2410-rtctick",rtc_dev);
  118. if(ret){
  119. dev_err(dev,"IRQ%derror%dn",s3c_rtc_tickno,ret);
  120. gototick_err;
  121. }
  122. tick_err:
  123. returnret;
  124. }
  125. staticvoids3c_rtc_release(structdevice*dev)
  126. {
  127. structplatform_device*pdev=to_platform_device(dev);
  128. structrtc_device*rtc_dev=platform_get_drvdata(pdev);
  129. /*donotclearAIEhere,itmaybeneededforwake*/
  130. s3c_rtc_setpie(dev,0);
  131. free_irq(s3c_rtc_tickno,rtc_dev);
  132. }
  133. staticconststructrtc_class_opss3c_rtcops={
  134. .open=s3c_rtc_open,
  135. .release=s3c_rtc_release,
  136. .read_time=s3c_rtc_gettime,
  137. .set_time=s3c_rtc_settime,
  138. .irq_set_freq=s3c_rtc_setfreq,
  139. .irq_set_state=s3c_rtc_setpie,
  140. };
  141. staticvoids3c_rtc_enable(structplatform_device*pdev,inten)
  142. {
  143. void__iomem*base=s3c_rtc_base;
  144. unsignedinttmp;
  145. if(s3c_rtc_base==NULL)
  146. return;
  147. if(!en){
  148. tmp=readb(base+S3C2410_RTCCON);
  149. writeb(tmp&~S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
  150. tmp=readb(base+S3C2410_TICNT);
  151. writeb(tmp&~S3C2410_TICNT_ENABLE,base+S3C2410_TICNT);
  152. }else{
  153. /*re-enablethedevice,andcheckitisok*/
  154. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0){
  155. dev_info(&pdev->dev,"rtcdisabled,re-enablingn");
  156. tmp=readb(base+S3C2410_RTCCON);
  157. writeb(tmp|S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
  158. }
  159. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CNTSEL)){
  160. dev_info(&pdev->dev,"removingRTCCON_CNTSELn");
  161. tmp=readb(base+S3C2410_RTCCON);
  162. writeb(tmp&~S3C2410_RTCCON_CNTSEL,base+S3C2410_RTCCON);
  163. }
  164. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CLKRST)){
  165. dev_info(&pdev->dev,"removingRTCCON_CLKRSTn");
  166. tmp=readb(base+S3C2410_RTCCON);
  167. writeb(tmp&~S3C2410_RTCCON_CLKRST,base+S3C2410_RTCCON);
  168. }
  169. }
  170. }
  171. staticint__devexits3c_rtc_remove(structplatform_device*dev)
  172. {
  173. structrtc_device*rtc=platform_get_drvdata(dev);
  174. platform_set_drvdata(dev,NULL);
  175. rtc_device_unregister(rtc);
  176. s3c_rtc_setpie(&dev->dev,0);
  177. s3c_rtc_setaie(0);
  178. iounmap(s3c_rtc_base);
  179. release_resource(s3c_rtc_mem);
  180. kfree(s3c_rtc_mem);
  181. return0;
  182. }
  183. staticint__devinits3c_rtc_probe(structplatform_device*pdev)
  184. {
  185. structrtc_device*rtc;
  186. structresource*res;
  187. intret;
  188. pr_debug("%s:probe=%pn",__func__,pdev);
  189. /*findtheIRQs*/
  190. s3c_rtc_tickno=platform_get_irq(pdev,1);
  191. if(s3c_rtc_tickno<0){
  192. dev_err(&pdev->dev,"noirqforrtctickn");
  193. return-ENOENT;
  194. }
  195. /*getthememoryregion*/
  196. res=platform_get_resource(pdev,IORESOURCE_MEM,0);
  197. if(res==NULL){
  198. dev_err(&pdev->dev,"failedtogetmemoryregionresourcen");
  199. return-ENOENT;
  200. }
  201. s3c_rtc_mem=request_mem_region(res->start,
  202. res->end-res->start+1,
  203. pdev->name);
  204. if(s3c_rtc_mem==NULL){
  205. dev_err(&pdev->dev,"failedtoreservememoryregionn");
  206. ret=-ENOENT;
  207. gotoerr_nores;
  208. }
  209. s3c_rtc_base=ioremap(res->start,res->end-res->start+1);
  210. if(s3c_rtc_base==NULL){
  211. dev_err(&pdev->dev,"failedioremap()n");
  212. ret=-EINVAL;
  213. gotoerr_nomap;
  214. }
  215. /*checktoseeifeverythingissetupcorrectly*/
  216. s3c_rtc_enable(pdev,1);
  217. pr_debug("s3c2410_rtc:RTCCON=%02xn",
  218. readb(s3c_rtc_base+S3C2410_RTCCON));
  219. s3c_rtc_setfreq(&pdev->dev,1);
  220. /*registerRTCandexit*/
  221. rtc=rtc_device_register("s3c",&pdev->dev,&s3c_rtcops,
  222. THIS_MODULE);
  223. if(IS_ERR(rtc)){
  224. dev_err(&pdev->dev,"cannotattachrtcn");
  225. ret=PTR_ERR(rtc);
  226. gotoerr_nortc;
  227. }
  228. rtc->max_user_freq=128;
  229. platform_set_drvdata(pdev,rtc);
  230. return0;
  231. err_nortc:
  232. s3c_rtc_enable(pdev,0);
  233. iounmap(s3c_rtc_base);
  234. err_nomap:
  235. release_resource(s3c_rtc_mem);
  236. err_nores:
  237. returnret;
  238. }
  239. staticstructplatform_drivers3c2410_rtc_driver={
  240. .probe=s3c_rtc_probe,
  241. .remove=__devexit_p(s3c_rtc_remove),
  242. .driver={
  243. .name="s3c2410-rtc",
  244. .owner=THIS_MODULE,
  245. },
  246. };
  247. staticchar__initdatabanner[]="S3C24XXRTC,(c)2004,2006SimtecElectronicsn";
  248. staticint__inits3c_rtc_init(void)
  249. {
  250. printk(banner);
  251. returnplatform_driver_register(&s3c2410_rtc_driver);
  252. }
  253. staticvoid__exits3c_rtc_exit(void)
  254. {
  255. platform_driver_unregister(&s3c2410_rtc_driver);
  256. }
  257. module_init(s3c_rtc_init);
  258. module_exit(s3c_rtc_exit);
  259. MODULE_DESCRIPTION("Mys3c2440RTCDriver");
  260. MODULE_AUTHOR("YanMing-yming0221@gmail.com");
  261. MODULE_LICENSE("GPL");
  262. MODULE_ALIAS("platform:s3c2410-rtc");

Makefile文件

  1. obj-m:=rtc.o
  2. KERNELDIR?=/arm/linux-2.6.28.7-2440
  3. PWD:=$(shellpwd)
  4. default:
  5. $(MAKE)-C$(KERNELDIR)M=$(PWD)modules
  6. clean:
  7. rm-f*.o*.ko*.order*.symvers

make后在目录下生成rtc.ko驱动,利用NFS挂在到目标板,insmod rtc.ko驱动就可以加载,执行hwclock命令,查看是否可以读取硬件的RTC。


评论


技术专区

关闭