s3c2410 RTC驅(qū)動(dòng)框架linux內(nèi)核源碼分析
掃描二維碼
隨時(shí)隨地手機(jī)看文章
/*********************************************************************************************************
*@Description:s3c2410的rtc驅(qū)動(dòng)的實(shí)現(xiàn),rtc(realtimeclock)實(shí)時(shí)時(shí)鐘的驅(qū)動(dòng)是個(gè)很好的
*理解如果編寫驅(qū)動(dòng)的硬件,它包括了最基本的硬中斷,軟中斷的底層機(jī)制;
*s3c2410的RTC驅(qū)動(dòng)的實(shí)現(xiàn)個(gè)人認(rèn)為更是對(duì)linux設(shè)備驅(qū)動(dòng)一個(gè)很好的例子,他是通過(guò)二層結(jié)構(gòu)來(lái)
*實(shí)現(xiàn)的一個(gè)驅(qū)動(dòng),上層是一個(gè)armcommon的公共層,對(duì)上提供標(biāo)準(zhǔn)的通用的RTC操作接口,下層由
*我們來(lái)實(shí)現(xiàn)針對(duì)自己的chip和自己要提供的功能來(lái)實(shí)現(xiàn)的一層驅(qū)動(dòng);
*
*@FileTree:
**********************************************************************************************************
linux-2.6.14.6
|
|--arch
||
||--arm
|||--mach-s3c2410
||||-devs.c//包含了對(duì)各個(gè)部件的resource的分配和定義,在這看rtc的資源;
|||--common
||||-rtctime.c//一個(gè)arm平臺(tái)的通用rtc函數(shù)層,它對(duì)上隱藏了各種soc的rtcdriver的區(qū)別;
|||--kernel
||||-time.c//內(nèi)核的初始化例程time_init()會(huì)調(diào)用的xxx_cmos_xxx函數(shù)的實(shí)現(xiàn);定義了全局自旋鎖rtc_lock用來(lái)串行化所有CPU對(duì)RTC的操作
|
|--drivers
||--char
|||-s3c2410-rtc.c//具體的s3c2410上的rtcchip的驅(qū)動(dòng)實(shí)現(xiàn),如果需要在arm平臺(tái)的
||板子上實(shí)現(xiàn)一個(gè)驅(qū)動(dòng),改寫它就ok了。
|
|--include
||--asm-arm
|||--arch-s3c2410
||||-regs-rtc.h//S3C2410InternalRTCregisterdefinitionrefertodatasheet;
|||-rtc.h//arm平臺(tái)rtc操作抽象層rtctime.c對(duì)應(yīng)的.h
||--linux
|||-time.h//mktime的實(shí)現(xiàn);
|||-rtc.h//公用RTC.h
*
*[小結(jié)]
*1) 提供給user的接口,在arch/arm/common/rtctime.c,include/asm-arm/rtc.h中實(shí)現(xiàn),調(diào)用操作硬件驅(qū)動(dòng)在 drivers/char/s3c2410-rtc.c,include/asm-arm/arch-s3c2410/regs-rtc.h實(shí)現(xiàn);
*2) 提供給kernel的接口,在arch/arm/kernel/time.c,include/linux/time.h中實(shí)現(xiàn),調(diào)用操作硬件驅(qū)動(dòng)在 drivers/char/s3c2410-rtc.c,include/asm-arm/arch-s3c2410/regs-rtc.h實(shí)現(xiàn);
**********************************************************************************************************
*
*@Author:liyangth@gmail.com
*
*@FunctionList:
*
*
*
*
*@Changelog:
*2007-06-24LiYangFirstversion
*
*@FQA:
*[50%]Q1.在驅(qū)動(dòng)中要將設(shè)備注冊(cè)到總線,必須將設(shè)備封裝成structdevice_driver;調(diào)查這個(gè)結(jié)構(gòu)體中的每個(gè)成員.
*[0%]Q2.在板子(什么類型)上什么樣的設(shè)備要用總線(什么類型)注冊(cè)?
*[90%]Q3.
*structdevice--總線設(shè)備
*structdevice_driver--總線設(shè)備驅(qū)動(dòng)
*structplatform_device--平臺(tái)設(shè)備
*structresource--平臺(tái)資源
*
*[!0%]Q4.初始化rtcregister的函數(shù)的后面的flag具體控制什么?(在s2s65a里是否可以用它控制是softResetorhardwareReset)
*
*[0%]Q5.什么時(shí)候調(diào)用suspend,resume?
**********************************************************************************************************/
/*****************************************************************************
*Structures&Unions&Enums(#typedef)
*/
/*[include/linux/device.h]
*總線設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體,將它注冊(cè)到板子的總線上
*/
staticstructdevice_drivers3c2410_rtcdrv={
.name="s3c2410-rtc",
.owner=THIS_MODULE,
.bus=&platform_bus_type,//總線類型,貌似不用管
.probe=s3c2410_rtc_probe,//自檢->初始化REG->注冊(cè)到上一層
.remove=s3c2410_rtc_remove,//注銷
.suspend=s3c2410_rtc_suspend,//[掛起???]
.resume=s3c2410_rtc_resume,//[重起???]
};
/*[/include/asm-arm/rtc.h]
*底層特別操作集,將他注冊(cè)到上層的armcommon操作層
*/
staticstructrtc_opss3c2410_rtcops={
.owner=THIS_MODULE,
.open=s3c2410_rtc_open,
.release=s3c2410_rtc_release,
.ioctl=s3c2410_rtc_ioctl,
.read_time=s3c2410_rtc_gettime,
.set_time=s3c2410_rtc_settime,
.read_alarm=s3c2410_rtc_getalarm,
.set_alarm=s3c2410_rtc_setalarm,
.proc=s3c2410_rtc_proc,
};
/*****************************************************************************
*GlobalVariables
*/
s3c2410-rtc.c
|
|/*IRQHandlers*/
|-s3c2410_rtc_alarmirq(intirq,void*id,structpt_regs*r)
||
||-rtc_update(1,RTC_AF|RTC_IRQF);//獲得中斷標(biāo)志,和喚醒read阻塞,異步通知;
|
|-s3c2410_rtc_tickirq(intirq,void*id,structpt_regs*r)
|
|/*Updatecontrolregisters,與硬件實(shí)現(xiàn)有關(guān),refertodatasheet*/
|-s3c2410_rtc_setaie(intto)
|-s3c2410_rtc_setpie(intto)
|-s3c2410_rtc_setfreq(intfreq)
|
|/*實(shí)現(xiàn)了要插到上層armcommon層的具體的硬件操作,來(lái)填充structrtc_ops,這個(gè)具體與硬件相關(guān)的操作集會(huì)用
|register_rtc注冊(cè)到上層的*/
|
|-s3c2410_rtc_gettime(structrtc_time*rtc_tm)
|
|-s3c2410_rtc_settime(structrtc_time*tm)
|
|-s3c2410_rtc_getalarm(structrtc_wkalrm*alrm)
|
|-s3c2410_rtc_setalarm(structrtc_wkalrm*alrm)
|
|/*
|*插入到上層ioctl中的ioctl,上層中已經(jīng)通過(guò)這個(gè)driver中的gettime,settime在其ioctl中實(shí)現(xiàn)了取得和設(shè)置時(shí)間
|*,和一些共同的ioctl操作了
|*所以我們?cè)谶@只要實(shí)現(xiàn)與硬件不同部分的ioctl操作*/
|-s3c2410_rtc_ioctl(unsignedintcmd,unsignedlongarg)
|
|-s3c2410_rtc_proc(char*buf)
|-s3c2410_rtc_open(void)
||
||1.注冊(cè)申請(qǐng)鬧鐘中斷ISQ--s3c2410_rtc_alarmirq
||2.周期中斷ISQ--s3c2410_rtc_tickirq
|
|-s3c2410_rtc_release(void)
|
|/*InitializeRTCRegs*/
|-s3c2410_rtc_enable(structdevice*dev,inten)
||
||-if(!en)
||/*beforepoweroff,theRTCENbitshouldbeclearedto
||0topreventinadvertentwritingintoRTCregisters.*/
||1.將控制R的RTCEN位清0;
||
||2.disableinterrupt.
||
||-else/*re-enablethedevice,andcheckitisok*/
||
||1.將控制R的RTCEN位致1。
||
||2.BCDcountselect.--0=MergeBCDcounters
||
||3.RTCclockcountreset.--0=noreset
||
|
|
|
|-s3c2410_rtc_probe(structdevice*dev)
||[??]structplatform_device*pdev=to_platform_device(dev);//通過(guò)這個(gè)設(shè)備找到它宿主平臺(tái)的大設(shè)備;
||structresource*res;
||
||/*findtheIRQs,RTC有2中中斷,周期中斷和鬧鐘中斷*/
||-s3c2410_rtc_tickno=platform_get_irq(pdev,1);//從平臺(tái)上取得一個(gè)IRQ號(hào)給這個(gè)設(shè)備;
||
||/*getthememoryregion*/
||-res=platform_get_resource(pdev,IORESOURCE_MEM,0);
||
||/*向內(nèi)核申請(qǐng)資源空間*/
||-s3c2410_rtc_mem=request_mem_region(res->start,res->end-res->start+1,
pdev->name);//res->start這些資源的分配和在哪個(gè)段下,可以看../mach-s3c2410/devs.c
||
||/*然后將物理地址映射到虛擬地址,這樣驅(qū)動(dòng)和內(nèi)核就可以看到設(shè)備的I/Oregs了*/
||-s3c2410_rtc_base=ioremap(res->start,res->end-res->start+1);
||
||/*初始化設(shè)備regs*/
||-s3c2410_rtc_enable(dev,1);//可以用后面這個(gè)1(這個(gè)flag在s2s65a中可以用ram0-7來(lái)保存,掉電不清的)來(lái)控制是softreset還是hardwarereset.
||
||-s3c2410_rtc_setfreq(s3c2410_rtc_freq);//設(shè)定RTC周期頻率;
||
||/*最關(guān)鍵的一步,將與具體不同的底層硬件相關(guān)的設(shè)備驅(qū)動(dòng)注冊(cè)給arm通用操作層common/rtctime.c*/
||-register_rtc(&s3c2410_rtcops);
|
|-s3c2410_rtc_remove(structdevice*dev)
||-unregister_rtc(&s3c2410_rtcops);//從上一層將s3c2410的rtc的devicedirveroperationset拔下來(lái)。
||
||-s3c2410_rtc_setpie(0);//disable周期中斷
||
||-s3c2410_rtc_setaie(0);//disablealarminterrupt
||
||-iounmap(s3c2410_rtc_base);
||
||-release_resource(s3c2410_rtc_mem);
||
||-kfree(s3c2410_rtc_mem);//[MQA]哪塊kmalloc了呢,為什么這要free?
|
|
|#ifdefCONFIG_PM//如果電源控制開關(guān)打開
|
|-s3c2410_rtc_suspend(structdevice*dev,pm_message_tstate,u32level)
||
||if(level==SUSPEND_POWER_DOWN)//SUSPEND_POWER_DOWN在include/linux/device.h(generic,centralizeddrivermodel)中定義,這個(gè)里面是否是對(duì)設(shè)備的一些公用的行為的操作宏的定義呢?
|||1.保存周期中斷寄存器的值;
|||
|||2.從RTC中讀出時(shí)間
|||-s3c2410_rtc_gettime(&tm);//[localvariable]structrtc_timetm;
|||
|||3.將從RTC取出的時(shí)間ConvertGregoriandatetosecondssince01-01-197000:00:00.
|||-rtc_tm_to_time(&tm,&time.tv_sec);//[arch/arm/common/rtctime.c]
|||
|||4.將系統(tǒng)時(shí)間和RTC時(shí)間的差值保存到s3c2410_rtc_delta里;
|||-save_time_delta(&s3c2410_rtc_delta,&time);
|||
|||5.啟動(dòng)RTC,注意這次啟動(dòng)后面的falg為0了;
|||-s3c2410_rtc_enable(dev,0);
|
|
|-s3c2410_rtc_resume(structdevice*dev,u32level)
||
||1.啟動(dòng)RTC,注意這次啟動(dòng)后面的falg為1了;
||-s3c2410_rtc_enable(dev,1);
||
||2.從RTC中讀出時(shí)間
||-s3c2410_rtc_gettime(&tm);
||
||3.轉(zhuǎn)換
||-rtc_tm_to_time(&tm,&time.tv_sec);
||
||4.利用在suspend中保存的delta來(lái)恢復(fù)系統(tǒng)時(shí)間
||-restore_time_delta(&s3c2410_rtc_delta,&time);
||
||5.恢復(fù)周期中斷寄存器的值;
|
|[LiY]
|suspend(暫停,掛起)和resume(恢復(fù),再開始)有點(diǎn)象關(guān)機(jī)前保存現(xiàn)場(chǎng)和開機(jī)后再恢復(fù)現(xiàn)場(chǎng)一樣;
|
|#else
|#defines3c2410_rtc_suspendNULL
|#defines3c2410_rtc_resumeNULL
|#endif
|
|
|module_init(s3c2410_rtc_init)
|-s3c2410_rtc_init
||-driver_register(&s3c2410_rtcdrv);
|||[purpose]registerdriverwithbus;
|
|module_exit(s3c2410_rtc_exit)
|-s3c2410_rtc_exit(void)
||-driver_unregister(&s3c2410_rtcdrv)
/*****************************************************************************
*ExternFunctionDetails
*/
/**[drivers/base/driver.c]
*driver_register-registerdriverwithbus
*@drv:drivertoregister
*
*Wepassoffmostoftheworktothebus_add_driver()call,
*sincemostofthethingswehavetododealwiththebus
*structures.
*
*Theoneinterestingaspectisthatwesetup@drv->unloaded
*asacompletionthatgetscompletewhenthedriverreference
*countreaches0.
*/
intdriver_register(structdevice_driver*drv)
{
klist_init(&drv->klist_devices,klist_devices_get,klist_devices_put);
init_completion(&drv->unloaded);
returnbus_add_driver(drv);
}
/**[drivers/base/platform.c]
*platform_get_irq-getanIRQforadevice
*@dev:platformdevice
*@num:IRQnumberindex
*/
intplatform_get_irq(structplatform_device*dev,unsignedintnum)
{
structresource*r=platform_get_resource(dev,IORESOURCE_IRQ,num);
returnr?r->start:0;
}
/**[drivers/base/platform.c]
*platform_get_resource-getaresourceforadevice
*@dev:platformdevice
*@type:resourcetype
*@num:resourceindex
*/
structresource*
platform_get_resource(structplatform_device*dev,unsignedinttype,
unsignedintnum)
{
inti;
for(i=0;i
structresource*r=&dev->resource[i];
if((r->flags&(IORESOURCE_IO|IORESOURCE_MEM|
IORESOURCE_IRQ|IORESOURCE_DMA))
==type)
if(num--==0)
returnr;
}
returnNULL;
}
/*[../include/linux/ioport.h]
*Resourcesaretree-like,allowing
*nestingetc..
*/
structresource{
constchar*name;
unsignedlongstart,end;
unsignedlongflags;
structresource*parent,*sibling,*child;
};
/**[arch/arm/kernel/time.c]
*save_time_delta-SavetheoffsetbetweensystemtimeandRTCtime
*@delta:pointertotimespectostoredelta
*@rtc:pointertotimespecforcurrentRTCtime
*
*ReturnadeltabetweenthesystemtimeandtheRTCtime,such
*thatsystemtimecanberestoredlaterwithrestore_time_delta()
*
*[LiY]返回一個(gè)系統(tǒng)時(shí)間和RTC時(shí)間的差值,在重起或系統(tǒng)去讀RTC后,可以再用這個(gè)差值(delta)來(lái)恢復(fù)系統(tǒng)時(shí)間(系統(tǒng)時(shí)間不一定非要和RTC時(shí)間相同的);
*/
voidsave_time_delta(structtimespec*delta,structtimespec*rtc)
{
set_normalized_timespec(delta,
xtime.tv_sec-rtc->tv_sec,
xtime.tv_nsec-rtc->tv_nsec);
}
/***[arch/arm/kernel/time.c]
*restore_time_delta-Restorethecurrentsystemtime
*@delta:deltareturnedbysave_time_delta()
*@rtc:pointertotimespecforcurrentRTCtime
*/
voidrestore_time_delta(structtimespec*delta,structtimespec*rtc)
{
structtimespects;
set_normalized_timespec(&ts,
delta->tv_sec+rtc->tv_sec,
delta->tv_nsec+rtc->tv_nsec);
do_settimeofday(&ts);
}
/*
*[arch/arm/common/rtctime.c]
*/
voidrtc_update(unsignedlongnum,unsignedlongevents)
{
spin_lock(&rtc_lock);
rtc_irq_data=(rtc_irq_data+(num<<8))|events;
spin_unlock(&rtc_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync(&rtc_async_queue,SIGIO,POLL_IN);
}