- Real Time Clock
RTC,实时时钟用于在系统电源关闭的情况下依靠备用电池工作,完成计时功能。
- 工作原理
以上是实时时钟的框架图,由XTIrtc和XTOrtc产生脉冲信号出,传给时钟分频器,得到128Hz的频率,用于产生滴答计数。当TICNT计数为0时,产生一个TIME TICK中断信号。RTCCON寄存器用来控制RTC的功能,RTCRST是重置寄存器,用来重置SEC和MIN寄存器。Leap Year Generator是润年发生器。RTCALM用来控制是否产生报警信号。
- RTC设备资源
drivers/rtc/rtc-s3c.c
/* RTC */
static struct resource s3c_rtc_resource[] = {
[0] = {/* io端口资源 */
.start = S3C24XX_PA_RTC,
.end = S3C24XX_PA_RTC + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {/* Alarm中断资源 */
.start = IRQ_RTC,
.end = IRQ_RTC,
.flags = IORESOURCE_IRQ,
},
[2] = {/* TIME TICK中断资源 */
.start = IRQ_TICK,
.end = IRQ_TICK,
.flags = IORESOURCE_IRQ
}
};struct platform_device s3c_device_rtc = {
.name = “s3c2410-rtc”,
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};EXPORT_SYMBOL(s3c_device_rtc); rtc设备资源在mach-mini2440.c中被注册到内核
- 加载卸载
static struct platform_driver s3c2410_rtc_driver = { .probe = s3c_rtc_probe, .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .driver = { .name = "s3c2410-rtc", .owner = THIS_MODULE, }, };
static char __initdata banner[] = “S3C24XX RTC, (c) 2004,2006 Simtec Electronicsn”;
static int __init s3c_rtc_init(void)
{ printk(banner);
return platform_driver_register(&s3c2410_rtc_driver);
}static void __exit s3c_rtc_exit(void)
{ platform_driver_unregister(&s3c2410_rtc_driver);
}module_init(s3c_rtc_init);
module_exit(s3c_rtc_exit); 这一部分代码向平台注册了平台驱动。 - RTC探测函数s3c_rtc_probe()
首先介绍一个重要的结构体 struct rtc_device
include/linux/rtc.hstruct rtc_device { struct device dev; //内嵌设备结构体 struct module *owner; //所属模块
int id; //设备id
char name[RTC_DEVICE_NAME_SIZE]; //RTC设备名const struct rtc_class_ops *ops; //类操作函数集
struct mutex ops_lock; //互斥锁struct cdev char_dev; //内嵌字符设备
unsigned long flags; //RTC状态标志unsigned long irq_data; //中断数据
spinlock_t irq_lock; //中断自旋锁
wait_queue_head_t irq_queue; //中断等待队列
struct fasync_struct *async_queue; //异步队列struct rtc_task *irq_task; //RTC任务结构体
spinlock_t irq_task_lock; //中断任务自旋锁
int irq_freq; //中断频率
int max_user_freq; //最大用户频率
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};以下是s3c_rtc_probe()
static int __devinit s3c_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct resource *res; int ret;
pr_debug(“%s: probe=%pn”, __func__, pdev);
/* find the IRQs */
/* 获得TIME TICK 中断号 */
s3c_rtc_tickno = platform_get_irq(pdev, 1);
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, “no irq for rtc tickn”);
return -ENOENT;
}/* 获得Alarm 中断号 */
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, “no irq for alarmn”);
return -ENOENT;
}pr_debug(“s3c2410_rtc: tick irq %d, alarm irq %dn”,
s3c_rtc_tickno, s3c_rtc_alarmno);/* get the memory region */
/* 获得io端口资源 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, “failed to get memory region resourcen”);
return -ENOENT;
} - RTC频率设置函数s3c_rtc_setfreq()
/* 申请io内存区域 */
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 regionn”);
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 */
/* 使能rtc */
s3c_rtc_enable(pdev, 1);pr_debug(“s3c2410_rtc: RTCCON=%02xn”,
readb(s3c_rtc_base + S3C2410_RTCCON));/* 设置rtc频率为1Hz */
s3c_rtc_setfreq(&pdev->dev, 1);/* 让驱动支持电源管理,arg2=1表示设备可以唤醒 */
device_init_wakeup(&pdev->dev, 1);/* 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 rtcn”);
ret = PTR_ERR(rtc);
goto err_nortc;
}rtc->max_user_freq = 128;
/* 保存rtc结构体 */
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;
} - RTC使能函数s3c_rtc_enable()
static void s3c_rtc_enable(struct platform_device *pdev, int en) { void __iomem *base = s3c_rtc_base; unsigned int tmp;
if (s3c_rtc_base == NULL)
return;if (!en) {
/* 禁止RTC */
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);/* 停止计数 */
tmp = readb(base + S3C2410_TICNT);
writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);
} else {
/* re-enable the device, and check it is ok *//* 使能RTC */
if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
dev_info(&pdev->dev, “rtc disabled, re-enablingn”);tmp = readb(base + S3C2410_RTCCON);
writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
}/* 使用合并的BCD码 */
if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
dev_info(&pdev->dev, “removing RTCCON_CNTSELn”);tmp = readb(base + S3C2410_RTCCON);
writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
}/* 禁止RTC重置位 */
if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
dev_info(&pdev->dev, “removing RTCCON_CLKRSTn”);tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
}
}
} - RTC频率设置函数s3c_rtc_setfreq()
/* 设置TIME TICK频率 */ static int s3c_rtc_setfreq(struct device *dev, int freq) { unsigned int tmp;
if (!is_power_of_2(freq))
return -EINVAL;/* 使用自旋锁保护临界资源 */
spin_lock_irq(&s3c_rtc_pie_lock);tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
/* n=(128 / freq )-1 由datasheet给出 */
tmp |= (128 / freq)-1;writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);return 0;
} - RTC卸载函数s3c_rtc_remove()
static int __devexit s3c_rtc_remove(struct platform_device *dev) { struct rtc_device *rtc = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);return 0;
} - 文件系统接口rtc设备文件操作集struct rtc_class_ops