ARM-Linux驅(qū)動--RTC(實時時鐘)驅(qū)動分析
內(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中,如下
structplatform_driver{ - int(*probe)(structplatform_device*);
- int(*remove)(structplatform_device*);
- void(*shutdown)(structplatform_device*);
- int(*suspend)(structplatform_device*,pm_message_tstate);
- int(*suspend_late)(structplatform_device*,pm_message_tstate);
- int(*resume_early)(structplatform_device*);
- int(*resume)(structplatform_device*);
- structpm_ext_ops*pm;
- structdevice_driverdriver;
- };
- staticstructplatform_drivers3c2410_rtc_driver={
- .probe=s3c_rtc_probe,//RTC探測函數(shù)
- .remove=__devexit_p(s3c_rtc_remove),//RTC移除函數(shù)
- .driver={
- .name="s3c2410-rtc",
- .owner=THIS_MODULE,
- },
- };
- staticint__inits3c_rtc_init(void)
- {
- printk(banner);
- returnplatform_driver_register(&s3c2410_rtc_driver);
- }
- staticvoid__exits3c_rtc_exit(void)
- {
- platform_driver_unregister(&s3c2410_rtc_driver);
- }
platform_driver_register()和platform_driver_unregister()函數(shù)在/drivers/base/platform.c中實現(xiàn)的。
可以看出,platform_driver_register()函數(shù)的作用就是為platform_driver中的driver中的probe、remove等提供接口函數(shù)
- intplatform_driver_register(structplatform_driver*drv)
- {
- drv->driver.bus=&platform_bus_type;
- if(drv->probe)
- drv->driver.probe=platform_drv_probe;
- if(drv->remove)
- drv->driver.remove=platform_drv_remove;
- if(drv->shutdown)
- drv->driver.shutdown=platform_drv_shutdown;
- if(drv->suspend)
- drv->driver.suspend=platform_drv_suspend;
- if(drv->resume)
- drv->driver.resume=platform_drv_resume;
- if(drv->pm)
- drv->driver.pm=&drv->pm->base;
- returndriver_register(&drv->driver);//注冊老的驅(qū)動
- }
- voidplatform_driver_unregister(structplatform_driver*drv)
- {
- driver_unregister(&drv->driver);
- }
接下來是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ū)動程序中一般都要完成。
- staticint__devinits3c_rtc_probe(structplatform_device*pdev)
- {
- structrtc_device*rtc;//定義rtc_device結(jié)構(gòu)體,定義在/include/linux/rtc.h
- structresource*res;//定義資源結(jié)構(gòu)體,定義在/include/linux/ioport.h
- intret;
- pr_debug("%s:probe=%pn",__func__,pdev);
- /*findtheIRQs*/
- s3c_rtc_tickno=platform_get_irq(pdev,1);//在系統(tǒng)定義的平臺設(shè)備中獲取中斷號
- if(s3c_rtc_tickno<0){//異常處理
- dev_err(&pdev->dev,"noirqforrtctickn");
- return-ENOENT;
- }
- /*getthememoryregion*/
- res=platform_get_resource(pdev,IORESOURCE_MEM,0);//獲取RTC平臺使用的IO資源
- if(res==NULL){
- dev_err(&pdev->dev,"failedtogetmemoryregionresourcen");
- return-ENOENT;
- }
- //申請內(nèi)存區(qū)域,res是structresource類型,見本函數(shù)后面
- s3c_rtc_mem=request_mem_region(res->start,
- res->end-res->start+1,
- pdev->name);
- if(s3c_rtc_mem==NULL){//申請內(nèi)存出錯
- dev_err(&pdev->dev,"failedtoreservememoryregionn");
- ret=-ENOENT;
- gotoerr_nores;
- }
- //將寄存器地址映射成虛擬地址,以便訪問
- s3c_rtc_base=ioremap(res->start,res->end-res->start+1);
- if(s3c_rtc_base==NULL){
- dev_err(&pdev->dev,"failedioremap()n");
- ret=-EINVAL;
- gotoerr_nomap;
- }
- /*checktoseeifeverythingissetupcorrectly*/
- s3c_rtc_enable(pdev,1);//對RTCCON寄存器設(shè)置,詳情見下面的函數(shù)實現(xiàn)
- pr_debug("s3c2410_rtc:RTCCON=%02xn",
- readb(s3c_rtc_base+S3C2410_RTCCON));
- s3c_rtc_setfreq(&pdev->dev,1);//詳情見下面的函數(shù)實現(xiàn)
- /*registerRTCandexit*/
- rtc=rtc_device_register("s3c",&pdev->dev,&s3c_rtcops,
- THIS_MODULE);//注冊RTC為RTC設(shè)備,其中s3c_rtcops定義見下
- if(IS_ERR(rtc)){
- dev_err(&pdev->dev,"cannotattachrtcn");
- ret=PTR_ERR(rtc);
- gotoerr_nortc;
- }
- rtc->max_user_freq=128;//設(shè)置RTC節(jié)拍時間計數(shù)寄存器TICNT的節(jié)拍時間計數(shù)值的用戶最大相對值
- //將RTC類的設(shè)備數(shù)據(jù)傳遞給系統(tǒng)設(shè)備,在/include/linux/platform_device.h中
- //#defineplatform_set_drvdata(_dev,data)
dev_set_drvdata(&(_dev)->dev,(data)),該函數(shù)在/include/linux/device.h中定義,見本函數(shù)下面
- platform_set_drvdata(pdev,rtc);
- return0;
- //異常處理
- err_nortc:
- s3c_rtc_enable(pdev,0);
- iounmap(s3c_rtc_base);
- err_nomap:
- release_resource(s3c_rtc_mem);
- err_nores:
- returnret;
- }
- structresource{
- resource_size_tstart;
- resource_size_tend;
- constchar*name;
- unsignedlongflags;
- structresource*parent,*sibling,*child;
- };
- staticinlinevoiddev_set_drvdata(structdevice*dev,void*data)
- {
- dev->driver_data=data;
- }
- staticvoids3c_rtc_enable(structplatform_device*pdev,inten)
- {
- void__iomem*base=s3c_rtc_base;//__iomem的作用就是為了使編譯器更好的優(yōu)化編譯
- unsignedinttmp;
- if(s3c_rtc_base==NULL)
- return;
- //en作為參數(shù)傳遞過來如果en==0,關(guān)閉電源前的情況
- if(!en){
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);//設(shè)置RTCCON寄存器,屏蔽RTC使能,可以參考數(shù)據(jù)手冊中寄存器的相關(guān)定義
- tmp=readb(base+S3C2410_TICNT);
- writeb(tmp&~S3C2410_TICNT_ENABLE,base+S3C2410_TICNT);//設(shè)置TICNT寄存器,屏蔽節(jié)拍時間中斷使能
- }else{
- /*re-enablethedevice,andcheckitisok*/
- //en!=0的情況,表示系統(tǒng)復(fù)位,重新使能RTC驅(qū)動
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0){//RTCCON第0位為0,將其設(shè)置為1,重新使能
- dev_info(&pdev->dev,"rtcdisabled,re-enablingn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp|S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
- }
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CNTSEL)){
- dev_info(&pdev->dev,"removingRTCCON_CNTSELn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_CNTSEL,base+S3C2410_RTCCON);//設(shè)置RTCCON第2位為0,設(shè)置BCD計數(shù)為混合BCD計數(shù)
- }
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CLKRST)){
- dev_info(&pdev->dev,"removingRTCCON_CLKRSTn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_CLKRST,base+S3C2410_RTCCON);//RTC時鐘計數(shù)器復(fù)位
- }
- }
- }
- staticints3c_rtc_setfreq(structdevice*dev,intfreq)//設(shè)定節(jié)拍時間計數(shù)值
- {
- unsignedinttmp;
- spin_lock_irq(&s3c_rtc_pie_lock);//獲取自旋鎖,對資源互斥訪問
- tmp=readb(s3c_rtc_base+S3C2410_TICNT)&S3C2410_TICNT_ENABLE;//節(jié)拍時間使能有效
- tmp|=(128/freq)-1;
- writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
- spin_unlock_irq(&s3c_rtc_pie_lock);//解鎖
- return0;
- }
下面是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)都在下面
- staticconststructrtc_class_opss3c_rtcops={
- .open=s3c_rtc_open,
- .release=s3c_rtc_release,
- .read_time=s3c_rtc_gettime,
- .set_time=s3c_rtc_settime,
- .irq_set_freq=s3c_rtc_setfreq,
- .irq_set_state=s3c_rtc_setpie,
- };
- staticints3c_rtc_open(structdevice*dev)
- {
- structplatform_device*pdev=to_platform_device(dev);//從平臺設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)
- structrtc_device*rtc_dev=platform_get_drvdata(pdev);
- intret;
- ret=request_irq(s3c_rtc_tickno,s3c_rtc_tickirq,
- IRQF_DISABLED,"s3c2410-rtctick",rtc_dev);//申請中斷
- if(ret){
- dev_err(dev,"IRQ%derror%dn",s3c_rtc_tickno,ret);
- gototick_err;
- }
- tick_err:
- returnret;
- }
- staticirqreturn_ts3c_rtc_tickirq(intirq,void*id)
- {
- structrtc_device*rdev=id;
- rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
- returnIRQ_HANDLED;
- }
- staticvoids3c_rtc_release(structdevice*dev)
- {
- structplatform_device*pdev=to_platform_device(dev);//從平臺設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)
- structrtc_device*rtc_dev=platform_get_drvdata(pdev);
- /*donotclearAIEhere,itmaybeneededforwake*/
- s3c_rtc_setpie(dev,0);//函數(shù)定義見下面
- free_irq(s3c_rtc_tickno,rtc_dev);
- }
- staticints3c_rtc_setpie(structdevice*dev,intenabled)
- {
- unsignedinttmp;
- pr_debug("%s:pie=%dn",__func__,enabled);
- spin_lock_irq(&s3c_rtc_pie_lock);
- tmp=readb(s3c_rtc_base+S3C2410_TICNT)&~S3C2410_TICNT_ENABLE;//讀取TICNT的值并將最高位清0
- if(enabled)
- tmp|=S3C2410_TICNT_ENABLE;
- writeb(tmp,s3c_rtc_base+S3C2410_TICNT);//寫入計算后新的值
- spin_unlock_irq(&s3c_rtc_pie_lock);
- return0;
- }
- staticints3c_rtc_gettime(structdevice*dev,structrtc_time*rtc_tm)
- {
- unsignedinthave_retried=0;
- void__iomem*base=s3c_rtc_base;
- retry_get_time:
- rtc_tm->tm_min=readb(base+S3C2410_RTCMIN);
- rtc_tm->tm_hour=readb(base+S3C2410_RTCHOUR);
- rtc_tm->tm_mday=readb(base+S3C2410_RTCDATE);
- rtc_tm->tm_mon=readb(base+S3C2410_RTCMON);
- rtc_tm->tm_year=readb(base+S3C2410_RTCYEAR);
- rtc_tm->tm_sec=readb(base+S3C2410_RTCSEC);
- /*theonlywaytoworkoutwetherthesystemwasmid-update
- *whenwereaditistocheckthesecondcounter,andifit
- *iszero,thenwere-trytheentireread
- */
- if(rtc_tm->tm_sec==0&&!have_retried){
- have_retried=1;
- gotoretry_get_time;
- }
- pr_debug("readtime%02x.%02x.%02x%02x/%02x/%02xn",
- rtc_tm->tm_year,rtc_tm->tm_mon,rtc_tm->tm_mday,
- rtc_tm->tm_hour,rtc_tm->tm_min,rtc_tm->tm_sec);
- rtc_tm->tm_sec=bcd2bin(rtc_tm->tm_sec);
- rtc_tm->tm_min=bcd2bin(rtc_tm->tm_min);
- rtc_tm->tm_hour=bcd2bin(rtc_tm->tm_hour);
- rtc_tm->tm_mday=bcd2bin(rtc_tm->tm_mday);
- rtc_tm->tm_mon=bcd2bin(rtc_tm->tm_mon);
- rtc_tm->tm_year=bcd2bin(rtc_tm->tm_year);
- rtc_tm->tm_year+=100;
- rtc_tm->tm_mon-=1;
- return0;
- }
- staticints3c_rtc_settime(structdevice*dev,structrtc_time*tm)
- {
- void__iomem*base=s3c_rtc_base;
- intyear=tm->tm_year-100;
- pr_debug("settime%02d.%02d.%02d%02d/%02d/%02dn",
- tm->tm_year,tm->tm_mon,tm->tm_mday,
- tm->tm_hour,tm->tm_min,tm->tm_sec);
- /*wegetaroundy2kbysimplynotsupportingit*/
- if(year<0||year>=100){
- dev_err(dev,"rtconlysupports100yearsn");
- return-EINVAL;
- }
- writeb(bin2bcd(tm->tm_sec),base+S3C2410_RTCSEC);
- writeb(bin2bcd(tm->tm_min),base+S3C2410_RTCMIN);
- writeb(bin2bcd(tm->tm_hour),base+S3C2410_RTCHOUR);
- writeb(bin2bcd(tm->tm_mday),base+S3C2410_RTCDATE);
- writeb(bin2bcd(tm->tm_mon+1),base+S3C2410_RTCMON);
- writeb(bin2bcd(year),base+S3C2410_RTCYEAR);
- return0;
- }
- #include
- #include
- #include
- #include
- #include
- #include
interrupt.h> - #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- staticstructresource*s3c_rtc_mem;
- staticvoid__iomem*s3c_rtc_base;
- staticints3c_rtc_tickno=NO_IRQ;
- staticDEFINE_SPINLOCK(s3c_rtc_pie_lock);
- staticirqreturn_ts3c_rtc_tickirq(intirq,void*id)
- {
- structrtc_device*rdev=id;
- rtc_update_irq(rdev,1,RTC_PF|RTC_IRQF);
- returnIRQ_HANDLED;
- }
- /*Updatecontrolregisters*/
- staticvoids3c_rtc_setaie(intto)
- {
- unsignedinttmp;
- pr_debug("%s:aie=%dn",__func__,to);
- tmp=readb(s3c_rtc_base+S3C2410_RTCALM)&~S3C2410_RTCALM_ALMEN;
- if(to)
- tmp|=S3C2410_RTCALM_ALMEN;
- writeb(tmp,s3c_rtc_base+S3C2410_RTCALM);
- }
- staticints3c_rtc_setpie(structdevice*dev,intenabled)
- {
- unsignedinttmp;
- pr_debug("%s:pie=%dn",__func__,enabled);
- spin_lock_irq(&s3c_rtc_pie_lock);
- tmp=readb(s3c_rtc_base+S3C2410_TICNT)&~S3C2410_TICNT_ENABLE;
- if(enabled)
- tmp|=S3C2410_TICNT_ENABLE;
- writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
- spin_unlock_irq(&s3c_rtc_pie_lock);
- return0;
- }
- staticints3c_rtc_setfreq(structdevice*dev,intfreq)
- {
- unsignedinttmp;
- spin_lock_irq(&s3c_rtc_pie_lock);
- tmp=readb(s3c_rtc_base+S3C2410_TICNT)&S3C2410_TICNT_ENABLE;
- tmp|=(128/freq)-1;
- writeb(tmp,s3c_rtc_base+S3C2410_TICNT);
- spin_unlock_irq(&s3c_rtc_pie_lock);
- return0;
- }
- /*Timeread/write*/
- staticints3c_rtc_gettime(structdevice*dev,structrtc_time*rtc_tm)
- {
- unsignedinthave_retried=0;
- void__iomem*base=s3c_rtc_base;
- retry_get_time:
- rtc_tm->tm_min=readb(base+S3C2410_RTCMIN);
- rtc_tm->tm_hour=readb(base+S3C2410_RTCHOUR);
- rtc_tm->tm_mday=readb(base+S3C2410_RTCDATE);
- rtc_tm->tm_mon=readb(base+S3C2410_RTCMON);
- rtc_tm->tm_year=readb(base+S3C2410_RTCYEAR);
- rtc_tm->tm_sec=readb(base+S3C2410_RTCSEC);
- /*theonlywaytoworkoutwetherthesystemwasmid-update
- *whenwereaditistocheckthesecondcounter,andifit
- *iszero,thenwere-trytheentireread
- */
- if(rtc_tm->tm_sec==0&&!have_retried){
- have_retried=1;
- gotoretry_get_time;
- }
- pr_debug("readtime%02x.%02x.%02x%02x/%02x/%02xn",
- rtc_tm->tm_year,rtc_tm->tm_mon,rtc_tm->tm_mday,
- rtc_tm->tm_hour,rtc_tm->tm_min,rtc_tm->tm_sec);
- rtc_tm->tm_sec=bcd2bin(rtc_tm->tm_sec);
- rtc_tm->tm_min=bcd2bin(rtc_tm->tm_min);
- rtc_tm->tm_hour=bcd2bin(rtc_tm->tm_hour);
- rtc_tm->tm_mday=bcd2bin(rtc_tm->tm_mday);
- rtc_tm->tm_mon=bcd2bin(rtc_tm->tm_mon);
- rtc_tm->tm_year=bcd2bin(rtc_tm->tm_year);
- rtc_tm->tm_year+=100;
- rtc_tm->tm_mon-=1;
- return0;
- }
- staticints3c_rtc_settime(structdevice*dev,structrtc_time*tm)
- {
- void__iomem*base=s3c_rtc_base;
- intyear=tm->tm_year-100;
- pr_debug("settime%02d.%02d.%02d%02d/%02d/%02dn",
- tm->tm_year,tm->tm_mon,tm->tm_mday,
- tm->tm_hour,tm->tm_min,tm->tm_sec);
- /*wegetaroundy2kbysimplynotsupportingit*/
- if(year<0||year>=100){
- dev_err(dev,"rtconlysupports100yearsn");
- return-EINVAL;
- }
- writeb(bin2bcd(tm->tm_sec),base+S3C2410_RTCSEC);
- writeb(bin2bcd(tm->tm_min),base+S3C2410_RTCMIN);
- writeb(bin2bcd(tm->tm_hour),base+S3C2410_RTCHOUR);
- writeb(bin2bcd(tm->tm_mday),base+S3C2410_RTCDATE);
- writeb(bin2bcd(tm->tm_mon+1),base+S3C2410_RTCMON);
- writeb(bin2bcd(year),base+S3C2410_RTCYEAR);
- return0;
- }
- staticints3c_rtc_open(structdevice*dev)
- {
- structplatform_device*pdev=to_platform_device(dev);
- structrtc_device*rtc_dev=platform_get_drvdata(pdev);
- intret;
- ret=request_irq(s3c_rtc_tickno,s3c_rtc_tickirq,
- IRQF_DISABLED,"s3c2410-rtctick",rtc_dev);
- if(ret){
- dev_err(dev,"IRQ%derror%dn",s3c_rtc_tickno,ret);
- gototick_err;
- }
- tick_err:
- returnret;
- }
- staticvoids3c_rtc_release(structdevice*dev)
- {
- structplatform_device*pdev=to_platform_device(dev);
- structrtc_device*rtc_dev=platform_get_drvdata(pdev);
- /*donotclearAIEhere,itmaybeneededforwake*/
- s3c_rtc_setpie(dev,0);
- free_irq(s3c_rtc_tickno,rtc_dev);
- }
- staticconststructrtc_class_opss3c_rtcops={
- .open=s3c_rtc_open,
- .release=s3c_rtc_release,
- .read_time=s3c_rtc_gettime,
- .set_time=s3c_rtc_settime,
- .irq_set_freq=s3c_rtc_setfreq,
- .irq_set_state=s3c_rtc_setpie,
- };
- staticvoids3c_rtc_enable(structplatform_device*pdev,inten)
- {
- void__iomem*base=s3c_rtc_base;
- unsignedinttmp;
- if(s3c_rtc_base==NULL)
- return;
- if(!en){
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
- tmp=readb(base+S3C2410_TICNT);
- writeb(tmp&~S3C2410_TICNT_ENABLE,base+S3C2410_TICNT);
- }else{
- /*re-enablethedevice,andcheckitisok*/
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_RTCEN)==0){
- dev_info(&pdev->dev,"rtcdisabled,re-enablingn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp|S3C2410_RTCCON_RTCEN,base+S3C2410_RTCCON);
- }
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CNTSEL)){
- dev_info(&pdev->dev,"removingRTCCON_CNTSELn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_CNTSEL,base+S3C2410_RTCCON);
- }
- if((readb(base+S3C2410_RTCCON)&S3C2410_RTCCON_CLKRST)){
- dev_info(&pdev->dev,"removingRTCCON_CLKRSTn");
- tmp=readb(base+S3C2410_RTCCON);
- writeb(tmp&~S3C2410_RTCCON_CLKRST,base+S3C2410_RTCCON);
- }
- }
- }
- staticint__devexits3c_rtc_remove(structplatform_device*dev)
- {
- structrtc_device*rtc=platform_get_drvdata(dev);
- platform_set_drvdata(dev,NULL);
- rtc_device_unregister(rtc);
- s3c_rtc_setpie(&dev->dev,0);
- s3c_rtc_setaie(0);
- iounmap(s3c_rtc_base);
- release_resource(s3c_rtc_mem);
- kfree(s3c_rtc_mem);
- return0;
- }
- staticint__devinits3c_rtc_probe(structplatform_device*pdev)
- {
- structrtc_device*rtc;
- structresource*res;
- intret;
- pr_debug("%s:probe=%pn",__func__,pdev);
- /*findtheIRQs*/
- s3c_rtc_tickno=platform_get_irq(pdev,1);
- if(s3c_rtc_tickno<0){
- dev_err(&pdev->dev,"noirqforrtctickn");
- return-ENOENT;
- }
- /*getthememoryregion*/
- res=platform_get_resource(pdev,IORESOURCE_MEM,0);
- if(res==NULL){
- dev_err(&pdev->dev,"failedtogetmemoryregionresourcen");
- return-ENOENT;
- }
- s3c_rtc_mem=request_mem_region(res->start,
- res->end-res->start+1,
- pdev->name);
- if(s3c_rtc_mem==NULL){
- dev_err(&pdev->dev,"failedtoreservememoryregionn");
- ret=-ENOENT;
- gotoerr_nores;
- }
- s3c_rtc_base=ioremap(res->start,res->end-res->start+1);
- if(s3c_rtc_base==NULL){
- dev_err(&pdev->dev,"failedioremap()n");
- ret=-EINVAL;
- gotoerr_nomap;
- }
- /*checktoseeifeverythingissetupcorrectly*/
- s3c_rtc_enable(pdev,1);
- pr_debug("s3c2410_rtc:RTCCON=%02xn",
- readb(s3c_rtc_base+S3C2410_RTCCON));
- s3c_rtc_setfreq(&pdev->dev,1);
- /*registerRTCandexit*/
- rtc=rtc_device_register("s3c",&pdev->dev,&s3c_rtcops,
- THIS_MODULE);
- if(IS_ERR(rtc)){
- dev_err(&pdev->dev,"cannotattachrtcn");
- ret=PTR_ERR(rtc);
- gotoerr_nortc;
- }
- rtc->max_user_freq=128;
- platform_set_drvdata(pdev,rtc);
- return0;
- err_nortc:
- s3c_rtc_enable(pdev,0);
- iounmap(s3c_rtc_base);
- err_nomap:
- release_resource(s3c_rtc_mem);
- err_nores:
- returnret;
- }
- staticstructplatform_drivers3c2410_rtc_driver={
- .probe=s3c_rtc_probe,
- .remove=__devexit_p(s3c_rtc_remove),
- .driver={
- .name="s3c2410-rtc",
- .owner=THIS_MODULE,
- },
- };
- staticchar__initdatabanner[]="S3C24XXRTC,(c)2004,2006SimtecElectronicsn";
- staticint__inits3c_rtc_init(void)
- {
- printk(banner);
- returnplatform_driver_register(&s3c2410_rtc_driver);
- }
- staticvoid__exits3c_rtc_exit(void)
- {
- platform_driver_unregister(&s3c2410_rtc_driver);
- }
- module_init(s3c_rtc_init);
- module_exit(s3c_rtc_exit);
- MODULE_DESCRIPTION("Mys3c2440RTCDriver");
- MODULE_AUTHOR("YanMing-yming0221@gmail.com");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("platform:s3c2410-rtc");
Makefile文件
- obj-m:=rtc.o
- KERNELDIR?=/arm/linux-2.6.28.7-2440
- PWD:=$(shellpwd)
- default:
- $(MAKE)-C$(KERNELDIR)M=$(PWD)modules
- clean:
- rm-f*.o*.ko*.order*.symvers
make后在目錄下生成rtc.ko驅(qū)動,利用NFS掛在到目標板,insmod rtc.ko驅(qū)動就可以加載,執(zhí)行hwclock命令,查看是否可以讀取硬件的RTC。
評論