Marvell-linux研究-pxa-rtc.c源代码分析
转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
我对RTC感兴趣的原因有两个,一是如何把修改后的时间保存下来,下次开机后,修改后的时间仍然有效,这要把修改后的时间写入RTC的寄存器中去。二是如何实现关机响闹和定时开机,这也要设置RTC的ALARM寄存器。
这里我们分析一下pxa-rtc.c的源代码
67 static irqreturn_t pxa_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) 68 { 69 unsigned int rtsr; 70 unsigned long num = 0, events = RTC_IRQF; 71 72 rtsr = RTSR; 73 74 /* clear interrupt sources */ 75 RTSR = 0; 76 RTSR = (RTSR_AL | RTSR_HZ | RTSR_RDAL1 | RTSR_RDAL2 | RTSR_SWAL1 | 77 RTSR_SWAL2 | RTSR_PIAL); 78 79 /* clear alarm interrupt if it has occurred */ 80 #ifdef CONFIG_PXA_RTC_WRISTWATCH 81 if (rtsr & (RTSR_RDAL1)) { 82 rtsr &= ~(RTSR_RDALE1 | RTSR_RDALE2); 83 num++; 84 events |= RTC_AF; 85 } 86 #else 87 if (rtsr & RTSR_AL) { 88 rtsr &= ~RTSR_ALE; 89 num++; 90 events |= RTC_AF; 91 } 92 #endif 93 if (rtsr & RTSR_HZ) { 94 /* rtsr &= ~RTSR_HZE; */ 95 num++; 96 events |= RTC_UF; 97 } 98 if (rtsr & RTSR_SWAL1) { 99 rtsr &= ~RTSR_SWALE1; 100 num++; 101 events |= RTC_SWF; 102 } 103 if (rtsr & RTSR_SWAL2) { 104 rtsr &= ~RTSR_SWALE2; 105 num++; 106 events |= RTC_SWF; 107 } 108 if (rtsr & RTSR_PIAL) { 109 /* rtsr &= ~RTSR_PIALE; */ 110 num++; 111 events |= RTC_PF; 112 } 113 RTSR = rtsr & (RTSR_ALE | RTSR_HZE | RTSR_RDALE1 | 114 RTSR_SWALE1 | RTSR_SWALE2 | RTSR_PIALE |RTSR_PICE | 115 RTSR_SWCE); 116 117 /* 118 printk(KERN_INFO "IRQ num:%d IRQ Events:0x%x/n", (int)num, 119 (unsigned int)events); 120 */ 121 /* update irq data & counter */ 122 rtc_update(num, events); 123 return IRQ_HANDLED; 124 }
|
72 读取RTSR中的值到rtsr变量。
75 禁止所有RTC中断。
76 清除所有RTC中断的状态。
81-112 这段代码检测是否发现了某种中断,若发生了则把计数加1,并设置相应事件位域。
113 恢复中断设置。
122 调用rtc_update更新rtc_irq_data,并唤醒相应的等待者。
127 static irqreturn_t pxa_rtc_tickirq(int irq, void *dev_id, struct pt_regs *regs) 128 { 129 unsigned long num = 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 */ 137 OSSR = OSSR_M1; /* clear match on timer1 */ 138 OSMR1 = TIMER_FREQ/rtc_freq + OSCR; 139 num++; 140 events |= RTC_PF; 141 142 /* update irq data & counter */ 143 rtc_update(num, events); 144 return IRQ_HANDLED; 145 }
|
137 清除标志。
138 设置下一次的中断时间,间隔时间长度主要用rtc_freq决定,该参数由用户设置。
139 增加事件计数。
140-143调用rtc_update更新rtc_irq_data,并唤醒相应的等待者。
148 static void tm_to_wwtime(struct rtc_time *tm, unsigned int *dreg, 149 unsigned int *yreg)
165 static int wwtime_to_tm(unsigned int dreg, unsigned int yreg, 166 struct rtc_time *tm)
184 static unsigned int tm_to_swtime(struct sw_time *tm)
195 static void swtime_to_tm(unsigned int swreg, struct sw_time *tm)
206 static int pxa_sw_get(struct sw_time *tm, unsigned long cmd, unsigned long arg)
227 static int pxa_sw_set(struct sw_time *tm, unsigned long cmd, unsigned long arg)
267 static int pxa_rtc_getalarm(struct rtc_wkalrm *alrm)
279 static int pxa_rtc_settime(struct rtc_time *tm)
297 static int pxa_rtc_setalarm(struct rtc_wkalrm *alrm)
|
以上函数都非常简单,主要是时间格式的转换,这里不再多说。
315 static int pxa_rtc_ioctl(unsigned int cmd, unsigned long arg)
|
提供了RTC比较完整的控制,直接操作寄存器,没有什么复杂的逻辑,这也就不列出代码了。
490 static struct rtc_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事件的。
500 static int pxa_rtc_probe(struct device *dev) 501 { 502 int ret; 503 504 /* find the IRQs */ 505 ret = request_irq(IRQ_RTC1Hz, pxa_rtc_interrupt, 506 SA_INTERRUPT, "RTC 1Hz", NULL); 507 if (ret) { 508 printk(KERN_ERR "RTC:IRQ %d already in use./n", IRQ_RTC1Hz); 509 goto IRQ_RTC1Hz_failed; 510 } 511 ret = request_irq(IRQ_RTCAlrm, pxa_rtc_interrupt, 512 SA_INTERRUPT, "RTC Alrm", NULL); 513 if (ret) { 514 printk(KERN_ERR "RTC:IRQ %d already in use./n", IRQ_RTCAlrm); 515 goto IRQ_RTCAlrm_failed; 516 } 517 #ifdef SOFT_IRQP 518 ret = request_irq (IRQ_OST1, pxa_rtc_tickirq, SA_INTERRUPT, "rtc timer", NULL); 519 if (ret) { 520 printk(KERN_ERR "rtc: IRQ %d already in use./n", IRQ_OST1); 521 goto IRQ_OST1_failed; 522 } 523 #endif 524 525 /* initialization */ 526 RTSR = 0; 527 528 /* register RTC */ 529 register_rtc(&pxa_rtc_ops); 530 return 0; 531 532 #ifdef SOFT_IRQP 533 IRQ_OST1_failed: 534 free_irq (IRQ_RTCAlrm, NULL); 535 #endif 536 IRQ_RTCAlrm_failed: 537 free_irq(IRQ_RTC1Hz, NULL); 538 IRQ_RTC1Hz_failed: 539 return -EBUSY; 540 }
|
先注册中断处理函数,初始化RTC,然后注册RTC设备。Pxa-rtc是RTC的一个种实现,它并不直接暴露给用户空间,而是在rtc.c中以标准的接口/dev/rtc提供给应用程序使用。
542 static int pxa_rtc_remove(struct device *dev) 543 { 544 unregister_rtc(&pxa_rtc_ops); 545 546 #ifdef SOFT_IRQP 547 free_irq (IRQ_OST1, NULL); 548 #endif 549 free_irq(IRQ_RTCAlrm, NULL); 550 free_irq(IRQ_RTC1Hz, NULL); 551 return 0; 552 }
|
注销RTC,注销中断处理函数。
568 static int pxa_rtc_suspend(struct device *dev, pm_message_t state, u32 level) 569 { 570 struct rtc_time tm; 571 struct timespec time; 572 573 if (level == SUSPEND_POWER_DOWN) { 574 memset(&time, 0, sizeof(struct timespec)); 575 576 pxa_rtc_gettime(&tm); 577 rtc_tm_to_time(&tm, &time.tv_sec); 578 save_time_delta(&pxa_rtc_delta, &time); 579 } 580 581 return 0; 582 } 583 584 static int pxa_rtc_resume(struct device *dev, u32 level) 585 { 586 struct rtc_time tm; 587 struct timespec time; 588 589 if (level == RESUME_POWER_ON) { 590 memset(&time, 0, sizeof(struct timespec)); 591 592 pxa_rtc_gettime(&tm); 593 rtc_tm_to_time(&tm, &time.tv_sec); 594 restore_time_delta(&pxa_rtc_delta, &time); 595 } 596 597 return 0; 598 }
|
电源管理函数,这里要注意的是,这里的save和restore并非两个相反的动作,save是保存当前系统时间与RTC时间的差值,而restore是用上次保存的时间差值和RTC的时间来恢复当前系统时间。
604 static struct device_driver pxa_rtc_drv = { 605 name: "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 613 static int __init pxa_rtc_init(void) 614 { 615 int ret; 616 ret = driver_register(&pxa_rtc_drv); 617 if (ret) { 618 printk(KERN_ERR "rtc: register error./n"); 619 } 620 printk(KERN_INFO "PXA Real Time Clock driver v" DRIVER_VERSION "/n"); 621 return 0; 622 } 623 624 static void __exit pxa_rtc_exit(void) 625 { 626 driver_unregister(&pxa_rtc_drv); 627 }
|
注册/注销设备驱动程序。
~~end~~