转载时请注明出处和作者联系方式
作者联系方式:李先静<xianjimli at hotmail dot com>
我对RTC感兴趣的原因有两个,一是如何把修改后的时间保存下来,下次开机后,修改后的时间仍然有效,这要把修改后的时间写入RTC的寄存器中去。二是如何实现关机响闹和定时开机,这也要设置RTC的ALARM寄存器。
这里我们分析一下pxa-rtc.c的源代码
67staticirqreturn_t pxa_rtc_interrupt(intirq,void*dev_id,structpt_regs *regs) 68{ 69unsignedintrtsr; 70unsignedlongnum =0, events = RTC_IRQF; 71 72rtsr = RTSR; 73 74/*clear interrupt sources */ 75RTSR =0; 76RTSR = (RTSR_AL | RTSR_HZ | RTSR_RDAL1 | RTSR_RDAL2 | RTSR_SWAL1 | 77RTSR_SWAL2 | RTSR_PIAL); 78 79/*clear alarm interrupt if it has occurred */ 80#ifdefCONFIG_PXA_RTC_WRISTWATCH 81if(rtsr & (RTSR_RDAL1)) { 82rtsr &= ~(RTSR_RDALE1 | RTSR_RDALE2); 83num++; 84events |= RTC_AF; 85} 86#else 87if(rtsr & RTSR_AL) { 88rtsr &= ~RTSR_ALE; 89num++; 90events |= RTC_AF; 91} 92#endif 93if(rtsr & RTSR_HZ) { 94/*rtsr &= ~RTSR_HZE; */ 95num++; 96events |= RTC_UF; 97} 98if(rtsr & RTSR_SWAL1) { 99rtsr &= ~RTSR_SWALE1; 100num++; 101events |= RTC_SWF; 102} 103if(rtsr & RTSR_SWAL2) { 104rtsr &= ~RTSR_SWALE2; 105num++; 106events |= RTC_SWF; 107} 108if(rtsr & RTSR_PIAL) { 109/*rtsr &= ~RTSR_PIALE; */ 110num++; 111events |= RTC_PF; 112} 113RTSR = rtsr & (RTSR_ALE | RTSR_HZE | RTSR_RDALE1 | 114RTSR_SWALE1 | RTSR_SWALE2 | RTSR_PIALE |RTSR_PICE | 115RTSR_SWCE); 116 117/* 118printk(KERN_INFO "IRQ num:%d IRQ Events:0x%x\n", (int)num, 119(unsigned int)events); 120*/ 121/*update irq data & counter */ 122rtc_update(num, events); 123returnIRQ_HANDLED; 124}
|
72读取RTSR中的值到rtsr变量。
75禁止所有RTC中断。
76清除所有RTC中断的状态。
81-112这段代码检测是否发现了某种中断,若发生了则把计数加1,并设置相应事件位域。
113恢复中断设置。
122调用rtc_update更新rtc_irq_data,并唤醒相应的等待者。
127staticirqreturn_t pxa_rtc_tickirq(intirq,void*dev_id,structpt_regs *regs) 128{ 129unsignedlongnum =0, events = RTC_IRQF; 130/* 131 * If we match for the first time, the periodic interrupt flag won't 132 * be set.If it is, then we did wrap around (very unlikely but 133 * still possible) and compute the amount of missed periods. 134 * The match reg is updated only when the data is actually retrieved 135 * to avoid unnecessary interrupts. 136 */ 137OSSR = OSSR_M1;/*clear match on timer1 */ 138OSMR1 = TIMER_FREQ/rtc_freq + OSCR; 139num++; 140events |= RTC_PF; 141 142/*update irq data & counter */ 143rtc_update(num, events); 144returnIRQ_HANDLED; 145}
|
137清除标志。
138设置下一次的中断时间,间隔时间长度主要用rtc_freq决定,该参数由用户设置。
139增加事件计数。
140-143调用rtc_update更新rtc_irq_data,并唤醒相应的等待者。
148staticvoidtm_to_wwtime(structrtc_time *tm,unsignedint*dreg, 149unsignedint*yreg)
165staticintwwtime_to_tm(unsignedintdreg,unsignedintyreg, 166structrtc_time *tm)
184staticunsignedinttm_to_swtime(structsw_time *tm)
195staticvoidswtime_to_tm(unsignedintswreg,structsw_time *tm)
206staticintpxa_sw_get(structsw_time *tm,unsignedlongcmd,unsignedlongarg)
227staticintpxa_sw_set(structsw_time *tm,unsignedlongcmd,unsignedlongarg)
267staticintpxa_rtc_getalarm(structrtc_wkalrm *alrm)
279staticintpxa_rtc_settime(structrtc_time *tm)
297staticintpxa_rtc_setalarm(structrtc_wkalrm *alrm)
|
以上函数都非常简单,主要是时间格式的转换,这里不再多说。
315staticintpxa_rtc_ioctl(unsignedintcmd,unsignedlongarg)
|
提供了RTC比较完整的控制,直接操作寄存器,没有什么复杂的逻辑,这也就不列出代码了。
490staticstructrtc_ops pxa_rtc_ops = { 491.owner= THIS_MODULE, 492.ioctl= pxa_rtc_ioctl, 493.proc = pxa_rtc_read_proc, 494.read_time= pxa_rtc_gettime, 495.set_time = pxa_rtc_settime, 496.read_alarm = pxa_rtc_getalarm, 497.set_alarm= pxa_rtc_setalarm, 498};
|
该结构定义了RTC的基本操作,其中pxa_rtc_settime就是用来更新时间到RTC中,而pxa_rtc_setalarm就是用来设置硬件ALARM事件的。
500staticintpxa_rtc_probe(structdevice *dev) 501{ 502intret; 503 504/*find the IRQs */ 505ret = request_irq(IRQ_RTC1Hz, pxa_rtc_interrupt, 506SA_INTERRUPT,"RTC 1Hz",NULL); 507if(ret) { 508printk(KERN_ERR"RTC:IRQ%dalready in use.\n", IRQ_RTC1Hz); 509gotoIRQ_RTC1Hz_failed; 510} 511ret = request_irq(IRQ_RTCAlrm, pxa_rtc_interrupt, 512SA_INTERRUPT,"RTC Alrm",NULL); 513if(ret) { 514printk(KERN_ERR"RTC:IRQ%dalready in use.\n", IRQ_RTCAlrm); 515gotoIRQ_RTCAlrm_failed; 516} 517#ifdef SOFT_IRQP 518ret = request_irq (IRQ_OST1, pxa_rtc_tickirq, SA_INTERRUPT,"rtc timer",NULL); 519if(ret) { 520printk(KERN_ERR"rtc: IRQ%dalready in use.\n", IRQ_OST1); 521gotoIRQ_OST1_failed; 522} 523#endif 524 525/*initialization */ 526RTSR =0; 527 528/*register RTC */ 529register_rtc(&pxa_rtc_ops); 530return0; 531 532#ifdef SOFT_IRQP 533IRQ_OST1_failed: 534free_irq (IRQ_RTCAlrm,NULL); 535#endif 536IRQ_RTCAlrm_failed: 537free_irq(IRQ_RTC1Hz,NULL); 538IRQ_RTC1Hz_failed: 539return-EBUSY; 540}
|
先注册中断处理函数,初始化RTC,然后注册RTC设备。Pxa-rtc是RTC的一个种实现,它并不直接暴露给用户空间,而是在rtc.c中以标准的接口/dev/rtc提供给应用程序使用。
542staticintpxa_rtc_remove(structdevice *dev) 543{ 544unregister_rtc(&pxa_rtc_ops); 545 546#ifdef SOFT_IRQP 547free_irq (IRQ_OST1,NULL); 548#endif 549free_irq(IRQ_RTCAlrm,NULL); 550free_irq(IRQ_RTC1Hz,NULL); 551return0; 552}
|
注销RTC,注销中断处理函数。
568staticintpxa_rtc_suspend(structdevice *dev, pm_message_t state, u32 level) 569{ 570structrtc_time tm; 571structtimespec time; 572 573if(level == SUSPEND_POWER_DOWN) { 574memset(&time,0,sizeof(structtimespec)); 575 576pxa_rtc_gettime(&tm); 577rtc_tm_to_time(&tm, &time.tv_sec); 578save_time_delta(&pxa_rtc_delta, &time); 579} 580 581return0; 582} 583 584staticintpxa_rtc_resume(structdevice *dev, u32 level) 585{ 586structrtc_time tm; 587structtimespec time; 588 589if(level == RESUME_POWER_ON) { 590memset(&time,0,sizeof(structtimespec)); 591 592pxa_rtc_gettime(&tm); 593rtc_tm_to_time(&tm, &time.tv_sec); 594restore_time_delta(&pxa_rtc_delta, &time); 595} 596 597return0; 598}
|
电源管理函数,这里要注意的是,这里的save和restore并非两个相反的动作,save是保存当前系统时间与RTC时间的差值,而restore是用上次保存的时间差值和RTC的时间来恢复当前系统时间。
604staticstructdevice_driver pxa_rtc_drv = { 605name:"pxa-rtc", 606.bus= &platform_bus_type, 607.probe= pxa_rtc_probe, 608.remove = pxa_rtc_remove, 609.suspend= pxa_rtc_suspend, 610.resume = pxa_rtc_resume, 611}; 612 613staticint__init pxa_rtc_init(void) 614{ 615intret; 616ret = driver_register(&pxa_rtc_drv); 617if(ret) { 618printk(KERN_ERR"rtc: register error.\n"); 619} 620printk(KERN_INFO"PXA Real Time Clock driver v"DRIVER_VERSION"\n"); 621return0; 622} 623 624staticvoid__exit pxa_rtc_exit(void) 625{ 626driver_unregister(&pxa_rtc_drv); 627}
|
注册/注销设备驱动程序。
~~end~~