RTC驱动程序——学习笔记

????一、RTC简介:
RTC(Real Time Clock)是为了给操作系统提供一个可靠的时间,且在断电情况下使用备用电池供电(即在断电情况下,也能确保时间的正确性)。
RTC是通过依靠一个频率为32.768KHZ的外部晶振,产生周期的脉冲信号,每一个脉冲信号到来,计数器(TICNT)减一,来完成计时功能的。
从下图可以看出来,XTIrtc和XTOrtc产生脉冲信号,传给2^15分频器,得到一个128HZ的信号,这个信号就是来产生滴答计数的。当TICNT为0时,产生TIME TICK中断信号。


下面来看RTC的各个寄存器:
1、RTCCON(RTC控制寄存器)
值得注意的是,RTCEN被设置为1时,才能对该寄存器进行读写。所以在电源关闭之前,应清零以防止无效数据写入。
??
2、TICNT:当设置为127的时候,就是每秒产生一次TIME TICK中断信号。

还有其余的设置时间与闹铃的寄存器,就不一一列举了。


二、RTC驱动程序:
在s3c_rtc_init(void)这个函数中,platform_driver_register(&s3c2410_rtcdrv)了一个平台设备驱动的
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
}; 结构体。
下面我们主要来看 s3c_rtc_probe这个函数。

static int s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
int ret;


pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);


/* find the IRQs */
s3c_rtc_tickno = platform_get_irq(pdev, 1);
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");
return -ENOENT;
}


s3c_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");
return -ENOENT;
}


pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
s3c_rtc_tickno, s3c_rtc_alarmno);


/* get the memory region */


res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");
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, "failed to reserve memory region\n");
ret = -ENOENT;
goto err_nores;
}


s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto err_nomap;
}
/* check to see if everything is setup correctly */


s3c_rtc_enable(pdev, 1);


pr_debug("s3c2410_rtc: RTCCON=%02x\n",
readb(s3c_rtc_base + S3C2410_RTCCON));


s3c_rtc_setfreq(s3c_rtc_freq);


/* register RTC and exit */


rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
THIS_MODULE);


if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}


rtc->max_user_freq = 128;


platform_set_drvdata(pdev, rtc);
return 0;


err_nortc:
s3c_rtc_enable(pdev, 0);
iounmap(s3c_rtc_base);


err_nomap:
release_resource(s3c_rtc_mem);


err_nores:
return ret;
}
说明:
前面的获取中断号、IO内存资源并映射到自己的地址空间,这些都是基本知识,就不详细说了。
然后看s3c_rtc_enable(pdev, 1); 这句,这里主要是想重启RTC一次,并把RTCCON的RTCEN置一,表示可读写这个寄存器,并将CNTSET和CLKRST位清零。
然后看s3c_rtc_setfreq(s3c_rtc_freq); static int s3c_rtc_freq= 1; 这里就是1秒产生一次TIME TICK中断的意思,主要就是修改RTCCNT的值为127。
然后再注册s3c_rtcops,下面我们来看这个结构体。

static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.ioctl = s3c_rtc_ioctl,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
};

在open中,最主要的就是request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,IRQF_DISABLED,"s3c2410-rtc alarm", rtc_dev); 这句,注册中断服务函数。
在注册的中断处理函数中,主要就是把irq_queue里面的等待进程唤醒。至于为什么会有进程等待在这个队列上?求高手解答。

剩下的ioctl、gettime等函数,主要是根据用户的参数设置寄存器了。

(注:本人是linux驱动学习的菜菜,希望高手如果发现文中有错,请不吝赐教。)

【RTC驱动程序——学习笔记】

    推荐阅读