RTC系统【转】
转自:http://www.cnblogs.com/muhuacat/p/5276306.html
一. RTC及驱动简介
RTC即real time clock实时时钟,主要用于为操作系统提供可靠的时间;当系统处于断电 的情况下,RTC记录操作系统时间,并可在电池供电情况下继续正常工作,当系统正常启动后,系统可从RTC读取时间信息,来确保断电后时间运行连续性。
目前,很多CPU中都已集成RTC系统,且有许多独立的外接RTC芯片可用于实现RTC功能;
在内核中RTC驱动可分为两层,一层为于硬件无关的抽象层,主要用于管理RTC设备、设备节点,属性节点注册及操作;另一层为与硬件相关的RTC低层驱动层;抽象层程序在/drivers/rtc目录下,主要涉及下面几个文件:
class.c 用于管理和注册RTC设备结构、sysfs、procfs及RTC类;
rtc-dev.c 用于注册和管理RTC设备节点,为用户空间提供devfs操作接口,主要操作有rtc_read,rtc_ioctl;
rtc-proc.c 用于管理rtc的procfs属性节点,提供一些中断状态、标志查询;
rtc-sysfs.c 用于管理rtc设备的sysfs属性,如获取RTC设备名字、日期、时间等属性信息;
interface.c 为rtc-dev.c 和RTC低层驱动提供操作接口;
RTC系统结构示意图如图1所示;
图1 RTC系统结构图
如图1所示,RTC 设备驱动通过rtc_device_register接口向linux系统注册RTC设备,并成生proc、sys目录下的属性文件;当用户通过/dev/rtcx设备节点或proc、sysfs接口来操作RTC设备时,都需要先通过interface接口才能访问真实的设备驱动。
二. 部分数据结构分析
1. RTC设备结构
struct rtc_device
{
struct device dev;
struct module *owner;
int id; 设备编号
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops; rtc设备低层操作接口;
struct mutex ops_lock;
struct cdev char_dev; RTC字符型设备结构;
unsigned long flags;
unsigned long irq_data; 中断数据;
spinlock_t irq_lock;
wait_queue_head_t irq_queue; 中断数据等待队列;
struct fasync_struct *async_queue;
struct rtc_task *irq_task;
spinlock_t irq_task_lock;
int irq_freq; 中断频率;
int max_user_freq;
struct timerqueue_head timerqueue;
struct rtc_timer aie_timer; 报警中断定时器;
struct rtc_timer uie_rtctimer; 更新中断定时器;
struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */ 周期中断高精度定时器;
int pie_enabled; 周期中断使能标志;
struct work_struct irqwork;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 内部仿真update interrupt时所需;
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
};
2. RTC设备低层操作接口
struct rtc_class_ops {
int (*open)(struct device *); 打开设备
void (*release)(struct device *); 释放设备
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *); 读取RTC时间;
int (*set_time)(struct device *, struct rtc_time *); 设置RTC时间;
int (*read_alarm)(struct device *, struct rtc_wkalrm *); 读取RTC报警时间;
int (*set_alarm)(struct device *, struct rtc_wkalrm *); 设置RTC报警时间;
int (*proc)(struct device *, struct seq_file *); 用于提供procfs查询rtc状态接口;
int (*set_mmss)(struct device *, unsigned long secs); 设置以S为单位RTC时间接口;
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled); 中断使能接口;
};
三.RTC低层驱动实现
1. 设备驱动注册及卸载
.probe = s3c_rtc_probe,.remove = s3c_rtc_remove,.suspend = s3c_rtc_suspend,.resume = s3c_rtc_resume,.id_table = s3c_rtc_driver_ids, 用于匹配平台设备.driver = {
.name = "s3c-rtc",.owner = THIS_MODULE,
},
return platform_driver_register(&s3c_rtc_driver)
platform_drvier_unregister(&s3c_rtc_driver)
[0] = {
.start = S3C_PA_RTC, RTC寄存器起始地址空间;.end = S3C_PA_RTC + 0xff,
.flags = IORESOURCE_MEM, resource为内存类型
},[1] = {
.start = IRQ_RTC_ALARM, RTC报警中断号.end = IRQ_RTC_ALARM,
.flags = IORESOURCE_IRQ, resource为中断类型
},[2] = {
.start = IRQ_RTC_TIC, RTC TICK中断.end = IRQ_RTC_TIC,
.flags = IORESOURCE_IRQ
}
struct platform_device s3c_device_rtc = {
.name = "s3c64xx-rtc",.id = -1,.num_resources = ARRAY_SIZE(s3c_rtc_resource),.resource = s3c_rtc_resource,
{
struct rtc_device *rtc;struct rtc_time rtc_tm;struct resource *res;int ret;pr_debug("%s: probe=%p\n", __func__, pdev);/* 从平台设备中获取相关的设备资源*/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;
}/*内核中,在对内存进行操作前得先申请内存空间,通过ioremap映射完后才能使用*/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, resource_size(res), 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, resource_size(res));if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");ret = -EINVAL;
goto err_nomap;
}/*获取并使能RTC 系统时钟信号*/rtc_clk = clk_get(&pdev->dev, "rtc");if (IS_ERR(rtc_clk)) {
dev_err(&pdev->dev, "failed to find rtc clock source\n");ret = PTR_ERR(rtc_clk);
rtc_clk = NULL;
goto err_clk;
}clk_enable(rtc_clk);/* 配置S3C RTC 控制寄存器,使能RTCEN,只有当RTCEN有效时才能配置BCD等寄存器,当系统断电前得清除RTCEN,防止对BCD寄存器进行写操作; */s3c_rtc_enable(pdev, 1);device_init_wakeup(&pdev->dev, 1);/*注册RTC设备,s3c_rtcops为需要实现的低层操作接口*/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;
}s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;/* Check RTC Time */s3c_rtc_gettime(NULL, &rtc_tm);if (rtc_valid_tm(&rtc_tm)) {
rtc_tm.tm_year = 100;rtc_tm.tm_mon = 0;
rtc_tm.tm_mday = 1;
rtc_tm.tm_hour = 0;
rtc_tm.tm_min = 0;
rtc_tm.tm_sec = 0;
s3c_rtc_settime(NULL, &rtc_tm);
dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
}if (s3c_rtc_cpu_type == TYPE_S3C64XX)
rtc->max_user_freq = 32768;
else
rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc);/*设置时钟频率为1HZ*/s3c_rtc_setfreq(&pdev->dev, 1);clk_disable(rtc_clk);return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);clk_disable(rtc_clk);clk_put(rtc_clk);
iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores:
return ret;
.open = s3c_rtc_open,.release = s3c_rtc_release,.read_time = s3c_rtc_gettime,.set_time = s3c_rtc_settime,.read_alarm = s3c_rtc_getalarm,.set_alarm = s3c_rtc_setalarm,.proc = s3c_rtc_proc,.alarm_irq_enable = s3c_rtc_setaie,
{
struct platform_device *pdev = to_platform_device(dev);struct rtc_device *rtc_dev = platform_get_drvdata(pdev);int ret;ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);return ret;
}ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);if (ret) {dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);goto tick_err;}return ret;
tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev);return ret;
{
struct platform_device *pdev = to_platform_device(dev);struct rtc_device *rtc_dev = platform_get_drvdata(pdev);free_irq(s3c_rtc_alarmno, rtc_dev);free_irq(s3c_rtc_tickno, rtc_dev);
{
unsigned int have_retried = 0;void __iomem *base = s3c_rtc_base;clk_enable(rtc_clk);
/*读取年、月、日、天、小时、分、秒数据*/
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);if (rtc_tm->tm_sec == 0 && !have_retried) {
have_retried = 1;goto retry_get_time;
}
/*将读取出来的数据进行BCD转二进制操作*/
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);rtc_tm->tm_year += 100;rtc_tm->tm_mon -= 1;clk_disable(rtc_clk);return rtc_valid_tm(rtc_tm);
{
void __iomem *base = s3c_rtc_base;int year = tm->tm_year - 100;clk_enable(rtc_clk);/* we get around y2k by simply not supporting it */if (year < 0 || year >= 100) {
dev_err(dev, "rtc only supports 100 years\n");return -EINVAL;
}/*将时间数据转成BCD码形式后存入到相应寄存器*/writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);writeb(bin2bcd(year), base + S3C2410_RTCYEAR);clk_disable(rtc_clk);return 0;
{
struct rtc_time *alm_tm = &alrm->time;void __iomem *base = s3c_rtc_base;unsigned int alm_en;clk_enable(rtc_clk);alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);alm_tm->tm_min = readb(base + S3C2410_ALMMIN);alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);alm_tm->tm_mon = readb(base + S3C2410_ALMMON);alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);alm_en = readb(base + S3C2410_RTCALM);alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;/* decode the alarm enable field */if (alm_en & S3C2410_RTCALM_SECEN)
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
else
alm_tm->tm_sec = -1;
if (alm_en & S3C2410_RTCALM_MINEN)
alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
else
alm_tm->tm_min = -1;
if (alm_en & S3C2410_RTCALM_HOUREN)
alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
else
alm_tm->tm_hour = -1;
if (alm_en & S3C2410_RTCALM_DAYEN)
alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
else
alm_tm->tm_mday = -1;
if (alm_en & S3C2410_RTCALM_MONEN) {
alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);alm_tm->tm_mon -= 1;
} else {
alm_tm->tm_mon = -1;
}if (alm_en & S3C2410_RTCALM_YEAREN)
alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
else
alm_tm->tm_year = -1;
clk_disable(rtc_clk);return 0;
{
struct rtc_time *tm = &alrm->time;void __iomem *base = s3c_rtc_base;unsigned int alrm_en;clk_enable(rtc_clk);alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;writeb(0x00, base + S3C2410_RTCALM);if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
alrm_en |= S3C2410_RTCALM_SECEN;writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);
}if (tm->tm_min < 60 && tm->tm_min >= 0) {
alrm_en |= S3C2410_RTCALM_MINEN;writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);
}if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
alrm_en |= S3C2410_RTCALM_HOUREN;writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
}writeb(alrm_en, base + S3C2410_RTCALM);s3c_rtc_setaie(dev, alrm->enabled);clk_disable(rtc_clk);return 0;
{
struct rtc_device *rdev = id;clk_enable(rtc_clk);rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);if (s3c_rtc_cpu_type == TYPE_S3C64XX)writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);clk_disable(rtc_clk);return IRQ_HANDLED;
3. 使用过程中出现问题及解决方法
“select() to /dev/rtc0 to wait for clock tick timed out.”
linux系统中/proc/interrupt可以用来查询中断号,中断使用次数等信息,由于S3C RTC中的中断申请是在s3c_rtc_open中,所以每次使用完就会在s3c_rtc_release中释放,所以无法在/proc/interrupt中查询到rtc相应信息,为了确定是否有中断产生,把中断申请和释放移到s3c_rtc_probe和s3c_rtc_remove函数中。更新程序后发现S3C RTC中的alarm 和tick中断相应的次数都为0,说明根本就没有产生中断。后来经过测试和分析发现,原来S3C RTC中的低层操作接口在每次设置完相应的寄存器后都会关闭RTC时钟,即clk_disable(rtc_clk);而没有RTC时钟就不会产生相应ALARM 和TICK中断,所以解决的方法就是使能RTC时钟;解决方法是在打开 RTC设备时,就一直使能时钟直到关闭设备;
RTC系统【转】的更多相关文章
- RTC系统
http://blog.csdn.net/fanqipin/article/details/8089995 一. RTC及驱动简介 RTC即real time clock实时时钟,主要用于为操作系统提 ...
- [RTC]系统时间NTP和RTC同步,Debian的时区配置
转自:http://www.cnblogs.com/jiu0821/p/5999566.html Debian的时区配置 一. 修改时区 1. 查看当前时区 命令 : "date -R&qu ...
- 吃我一记咸鱼突刺——使用板载RTC定时开机
前言 原创文章,转载引用务必注明链接.水平有限,欢迎指正. 2016年3月30日 Lemuntu(Base On Jessie) 3.10.37 原载于Lemaker论坛.汇总于此. 看ATC2603 ...
- 扩展 GRTN:云原生趋势下的 RTC 架构演进
在 2021 LiveVideoStackCon 音视频技术大会上海站,聚焦 "轻端重云和边缘架构新模式" 专场,阿里云视频云的 RTC 传输专家杨成立(忘篱)带来 "基 ...
- Linux-NTP-Server+Client
GMT/UTC/CST;/etc/localtime,/usr/share/zoneinfo/*时区文件,/etc/profile加TZ变量;硬件时间RTC,系统时间;date,hwclock,tzs ...
- 上手 WebRTC DTLS 遇到很多 BUG?浅谈 DTLS Fragment
上一篇<详解 WebRTC 传输安全机制:一文读懂 DTLS 协议>详细阐述了 DTLS.本文将结合 DTLS 开发中遇到的问题,详细解读 DTLS 的一些基础概念以及 Fragment ...
- ZEGO音视频服务的高可用架构设计与运营
前言: ZEGO 即构科技作为一家实时音视频的提供商,系统稳定性直接影响用户的主观体验,如何保障服务高可用且用户体验最优是行业面临的挑战,本文结合实际业务场景进行思考,介绍 ZEGO 即构在高可用架构 ...
- 2012高校GIS论坛
江苏省会议中心 南京·钟山宾馆(2012年4月21-22日) 以"突破与提升"为主题的"2012高校GIS论坛"将于4月在南京举行,由南京大学和工程中心共同承办 ...
- Linux系统时间与RTC时间【转】
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...
随机推荐
- IOS常用加密Encryption
NSString+Encryption.h // // NSString+Encryption.h // haochang // // Created by Administrator on 14-4 ...
- 如何让Asp.net Web Api全局预防Xss攻击
一.概述 二.什么是XSS 三.预防方法 四.在WebApi中如何实现 在实现之前,需要了解ASP.NET WEB API的pipeline机制. 如上,可以采用多种方式进行参数的过滤 1.重写Del ...
- pcxFirefox 自定义
便携特性(ini设置) 把与firefox.exe同文件夹的tmemutil-sample.ini 改名为tmemutil.ini,设置如下: Portable=1 #便携式 Portable ...
- 防止IE7,8进入怪异模式
在页头添加 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- shell ulimit -n
通过ulimit -n命令可以查看linux系统里打开文件描述符的最大值,一般缺省值是1024,
- hibernate学习笔记--可选的配置属性
3.4. 可选的配置属性 有大量属性能用来控制Hibernate在运行期的行为. 它们都是可选的, 并拥有适当的默认值. 警告: 其中一些属性是"系统级(system-level)的&qu ...
- C# Winform常见的Editor及其它经验
1.新建一个自定义Editor,继承自.NET自带的Editor,override某些方法,再附加到属性中: public class MyCollectionEditor : CollectionE ...
- JavaScript之With语句讲解
有了With 语句,在存取对象属性和方法时就不用重复指定参考对象,在 With 语句块中,凡是 JavaScript 不识别的属性和方法都和该语句块指定的对象有关.With 语句的语法格式如下所示:W ...
- POJ 2041
#include <iostream> #include <string> #include <algorithm> using namespace std; st ...
- CSS 的overflow:hidden 属性详细解释
overflow:hidden这个CSS样式是大家常用到的CSS样式,但是大多数人对这个样式的理解仅仅局限于隐藏溢出, 而对于清除浮动这个含义不是很了解.一提到清除浮动,我们就会想到另外一个CSS样式 ...