新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM-Linux驅動--RTC(實時時鐘)驅動分析

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

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

        內核版本:Linux 2.6.28

        本文引用地址:http://www.104case.com/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。其中的數據包括秒、分、時、日期、天、月、年的時間信息??梢詧绦袌缶δ堋?/p>

        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。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 罗平县| 阿克| 丰宁| 丁青县| 永丰县| 花垣县| 七台河市| 嵊泗县| 石景山区| 岗巴县| 唐海县| 河源市| 博乐市| 茂名市| 安康市| 澄城县| 大名县| 唐河县| 宿松县| 闵行区| 定结县| 五莲县| 信阳市| 抚松县| 阿坝| 疏附县| 宁津县| 沈阳市| 静宁县| 甘谷县| 濮阳市| 胶南市| 仁布县| 贵溪市| 西和县| 红桥区| 奉化市| 浦县| 麻阳| 新民市| 嘉善县|