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~~