新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > ARM-Linux驅(qū)動--RTC(實時時鐘)驅(qū)動分析

ARM-Linux驅(qū)動--RTC(實時時鐘)驅(qū)動分析

作者: 時間:2016-11-20 來源:網(wǎng)絡(luò) 收藏
硬件平臺:FL2440(S3C2440)

內(nèi)核版本:Linux 2.6.28

本文引用地址:http://m.butianyuan.cn/article/201611/318867.htm

主機平臺:Ubuntu 11.04

內(nèi)核版本:Linux 2.6.39

交叉編譯器版本:arm-linux-gcc 3.4.1

原創(chuàng)作品,轉(zhuǎn)載請標明出處http://blog.csdn.net/yming0221/article/details/6584285

1、實時時鐘概述

實時時鐘(RTC)單元可以在斷電的情況下使用紐扣電池繼續(xù)計時工作。RTC使用STRB/LDRB ARM操作傳輸二進制碼十進制數(shù)的8位數(shù)據(jù)給CPU。其中的數(shù)據(jù)包括秒、分、時、日期、天、月、年的時間信息??梢詧?zhí)行報警功能。

2、實時時鐘操作

下面是RTC模塊的電路圖

3、RTC寄存器介紹

實時時鐘控制寄存器(RTCCON)-REAL TIME CLOCK CONTROL REGISTER

節(jié)拍時間計數(shù)寄存器(TICNT)-TICK TIME COUNT REGISTER

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

報警秒數(shù)寄存器(ALMSEC)-ALARM SECOND DATA REGISTER

報警分鐘計數(shù)寄存器(ALMMIN)-ALARM MIN DATA REGISTER

報警小時數(shù)據(jù)寄存器(ALMHOUR)-ALARM HOUR DATA REGISTER

報警日期數(shù)據(jù)寄存器(ALMDATE)-ALARM DATE DATA REGISTER

報警月數(shù)數(shù)據(jù)寄存器(ALMMON)-ALARM MON DATA REGISTER

報警年數(shù)數(shù)據(jù)寄存器(ALMYEAR)-ALARM YEAR DATA REGISTER

BCD數(shù)據(jù)寄存器的格式和報警寄存器結(jié)構(gòu)相同,只是對應(yīng)的地址不同。

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、驅(qū)動實例分析

為了使驅(qū)動更容易理解,現(xiàn)在這個RTC驅(qū)動只完成了計時功能,沒有添加相應(yīng)的報警功能,也沒有添加電源管理的功能,缺少的功能今后完善。

下面先總體了解驅(qū)動:

首先是RTC驅(qū)動的結(jié)構(gòu)體,在/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. };
驅(qū)動中定義對應(yīng)的結(jié)構(gòu)體

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

[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()函數(shù)在/drivers/base/platform.c中實現(xiàn)的。

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

[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);//注冊老的驅(qū)動
  17. }
[cpp]view plaincopy
  1. voidplatform_driver_unregister(structplatform_driver*drv)
  2. {
  3. driver_unregister(&drv->driver);
  4. }

接下來是RTC平臺驅(qū)動探測函數(shù)s3c_rtc_probe,下面函數(shù)定義的時候使用了__devinit的作用是使編譯器優(yōu)化代碼,將其放在和是的內(nèi)存位置,減少內(nèi)存占用和提高內(nèi)核效率。

probe函數(shù)接收到plarform_device這個參數(shù)后,就需要從中提取出需要的信息。它一般會通過調(diào)用內(nèi)核提供的platform_get_resource和platform_get_irq等函數(shù)來獲得相關(guān)信息。如通過platform_get_resource獲得設(shè)備的起始地址后,可以對其進行request_mem_region和ioremap等操作,以便應(yīng)用程序?qū)ζ溥M行操作。通過platform_get_irq得到設(shè)備的中斷號以后,就可以調(diào)用request_irq函數(shù)來向系統(tǒng)申請中斷。這些操作在設(shè)備驅(qū)動程序中一般都要完成。

[cpp]view plaincopy
  1. staticint__devinits3c_rtc_probe(structplatform_device*pdev)
  2. {
  3. structrtc_device*rtc;//定義rtc_device結(jié)構(gòu)體,定義在/include/linux/rtc.h
  4. structresource*res;//定義資源結(jié)構(gòu)體,定義在/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);//在系統(tǒng)定義的平臺設(shè)備中獲取中斷號
  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. //申請內(nèi)存區(qū)域,res是structresource類型,見本函數(shù)后面
  20. s3c_rtc_mem=request_mem_region(res->start,
  21. res->end-res->start+1,
  22. pdev->name);
  23. if(s3c_rtc_mem==NULL){//申請內(nèi)存出錯
  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寄存器設(shè)置,詳情見下面的函數(shù)實現(xiàn)
  37. pr_debug("s3c2410_rtc:RTCCON=%02xn",
  38. readb(s3c_rtc_base+S3C2410_RTCCON));
  39. s3c_rtc_setfreq(&pdev->dev,1);//詳情見下面的函數(shù)實現(xiàn)
  40. /*registerRTCandexit*/
  41. rtc=rtc_device_register("s3c",&pdev->dev,&s3c_rtcops,
  42. THIS_MODULE);//注冊RTC為RTC設(shè)備,其中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;//設(shè)置RTC節(jié)拍時間計數(shù)寄存器TICNT的節(jié)拍時間計數(shù)值的用戶最大相對值
  49. //將RTC類的設(shè)備數(shù)據(jù)傳遞給系統(tǒng)設(shè)備,在/include/linux/platform_device.h中
[cpp]view plaincopy
  1. //#defineplatform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)->dev,(data)),該函數(shù)在/include/linux/device.h中定義,見本函數(shù)下面
[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結(jié)構(gòu)體定義
[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()的函數(shù)定義:

[cpp]view plaincopy
  1. staticinlinevoiddev_set_drvdata(structdevice*dev,void*data)
  2. {
  3. dev->driver_data=data;
  4. }
接下來是在s3c_rtc_probe()函數(shù)用到的兩個函數(shù)s3c_rtc_enable()和s3c_rtc_setfreq()

  1. staticvoids3c_rtc_enable(structplatform_device*pdev,inten)
  2. {
  1. void__iomem*base=s3c_rtc_base;//__iomem的作用就是為了使編譯器更好的優(yōu)化編譯
  2. unsignedinttmp;
  3. if(s3c_rtc_base==NULL)
  4. return;
  5. //en作為參數(shù)傳遞過來如果en==0,關(guān)閉電源前的情況
  6. if(!en){
  7. tmp=readb(base+S3C2410_RTCCON);
  1. writeb(tmp&~S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);//設(shè)置RTCCON寄存器,屏蔽RTC使能,可以參考數(shù)據(jù)手冊中寄存器的相關(guān)定義
  2. tmp=readb(base+S3C2410_TICNT);
  3. writeb(tmp&~S3C2410_TICNT_ENABLE,base+S3C2410_TICNT);//設(shè)置TICNT寄存器,屏蔽節(jié)拍時間中斷使能
  4. }else{
  5. /*re-enablethedevice,andcheckitisok*/
  6. //en!=0的情況,表示系統(tǒng)復(fù)位,重新使能RTC驅(qū)動
  7. if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0){//RTCCON第0位為0,將其設(shè)置為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);//設(shè)置RTCCON第2位為0,設(shè)置BCD計數(shù)為混合BCD計數(shù)
  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時鐘計數(shù)器復(fù)位
  21. }
  22. }
  23. }
[cpp]view plaincopy
  1. staticints3c_rtc_setfreq(structdevice*dev,intfreq)//設(shè)定節(jié)拍時間計數(shù)值
  2. {
  3. unsignedinttmp;
  4. spin_lock_irq(&s3c_rtc_pie_lock);//獲取自旋鎖,對資源互斥訪問
  5. tmp=readb(s3c_rtc_base+S3C2410_TICNT)&S3C2410_TICNT_ENABLE;//節(jié)拍時間使能有效
  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設(shè)備類的操作。

下面是rtc_class_ops是RTC設(shè)備類在RTC驅(qū)動核心部分中定義的對RTC設(shè)備類進行操作的結(jié)構(gòu)體,類似字符設(shè)備在驅(qū)動中的file_operations對字符設(shè)備進行操作的意思。該結(jié)構(gòu)體被定義在rtc.h中,對RTC的操作主要有打開、關(guān)閉、設(shè)置或獲取時間、設(shè)置或獲取報警、設(shè)置節(jié)拍時間計數(shù)值等等,該結(jié)構(gòu)體內(nèi)接口函數(shù)的實現(xiàn)都在下面

  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打開設(shè)備函數(shù)s3c_rtc_open()
  1. staticints3c_rtc_open(structdevice*dev)
  2. {
  3. structplatform_device*pdev=to_platform_device(dev);//從平臺設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)
  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節(jié)拍時間中斷服務(wù)程序
  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關(guān)閉設(shè)備函數(shù)s3c_rtc_release()

  1. staticvoids3c_rtc_release(structdevice*dev)
  2. {
  3. structplatform_device*pdev=to_platform_device(dev);//從平臺設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)
  4. structrtc_device*rtc_dev=platform_get_drvdata(pdev);
  5. /*donotclearAIEhere,itmaybeneededforwake*/
  6. s3c_rtc_setpie(dev,0);//函數(shù)定義見下面
  7. free_irq(s3c_rtc_tickno,rtc_dev);
  8. }
s3c_rtc_setpie()函數(shù),該函數(shù)主要作用就是根據(jù)參數(shù)設(shè)置TICNT寄存器的最高位,參數(shù)為0,禁止使能,參數(shù)為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. }
下面兩個函數(shù)是設(shè)置和讀取BCD寄存器的時間,邏輯很簡單,只是讀取和設(shè)置相應(yīng)寄存器的值

  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驅(qū)動的計時功能實現(xiàn),報警功能還沒有完成。下面是這個驅(qū)動源代碼

  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驅(qū)動,利用NFS掛在到目標板,insmod rtc.ko驅(qū)動就可以加載,執(zhí)行hwclock命令,查看是否可以讀取硬件的RTC。


評論


技術(shù)專區(qū)

關(guān)閉