????一、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驱动程序——学习笔记】
推荐阅读
- RTC驱动程序
- 驱动程序|Linux的LCD驱动
- linux驱动|基于老罗的freg案例,使用NDK工具调用驱动流程详细分析
- file_operation
- Android之LCD屏驱动
- 嵌入式基础|AM437x——RTC驱动
- android底层驱动学习之从应用程序如何到底层driver的调用
- TP多点触摸协议
- windows|LVDS与eDP的区别
- LCD MIPI DSI时钟计算