一、RTC设备驱动分析

内核的rtc驱动位于内核drivers/rtc目录下,里面包含各个平台的RTC驱动。读者可在此目录下任意选择一个单板驱动文件进行分析,我选择的是rtc-davinci.c文件。

文件链接:

https://files.cnblogs.com/files/Lioker/21_rtc.zip

首先来看init()函数:

 static struct platform_driver davinci_rtc_driver = {
.probe = davinci_rtc_probe,
.remove = __devexit_p(davinci_rtc_remove),
.driver = {
.name = "rtc_davinci",
.owner = THIS_MODULE,
},
}; static int __init rtc_init(void)
{
return platform_driver_probe(&davinci_rtc_driver, davinci_rtc_probe);
}

它注册了davinci_rtc_driver驱动,它对应的设备在arch/arm/mach-davinci/dm365.c中有定义:

 static struct resource dm365_rtc_resources[] = {
{
.start = DM365_RTC_BASE,
.end = DM365_RTC_BASE + SZ_1K - ,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_DM365_RTCINT,
.flags = IORESOURCE_IRQ,
},
}; static struct platform_device dm365_rtc_device = {
.name = "rtc_davinci",
.id = ,
.num_resources = ARRAY_SIZE(dm365_rtc_resources),
.resource = dm365_rtc_resources,
};

probe()函数调用关系如下:

davinci_rtc_probe
-> davinci_rtc->irq = platform_get_irq(pdev, ); /* 获取platform_device中断 */
-> res = platform_get_resource(pdev, IORESOURCE_MEM, ); /* 获取内存地址 */
-> mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size, pdev->name);
-> davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size); /* 对寄存器进行映射 */
/* 注册RTC设备 */
-> davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &davinci_rtc_ops, THIS_MODULE);
-> rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
-> rtc->id = id; /* 设置rtc成员 */
-> rtc->ops = ops; /* 这个就是rtc的操作函数 */
-> rtc_dev_prepare(rtc);
-> cdev_init(&rtc->char_dev, &rtc_dev_fops); /* 绑定file_operations */
-> device_register(&rtc->dev);
-> device_add(dev);
-> rtc_dev_add_device(rtc); /* 在/dev下创建rtc文件,将cdev添加到系统中 */
-> cdev_add(&rtc->char_dev, rtc->dev.devt, )
-> rtcif_write(davinci_rtc, , PRTCIF_INTEN); /* 设置寄存器 */
-> request_irq(davinci_rtc->irq, davinci_rtc_interrupt, , "davinci_rtc", davinci_rtc); /* 请求中断 */

probe()函数所做的有以下几点:

1. 设置rtc相关寄存器

2. 分配、设置并注册rtc_device

3. 分配、设置并注册cdev

在注册rtc_device过程中,rtc_device绑定了struct rtc_class_ops davinci_rtc_ops

static struct rtc_class_ops davinci_rtc_ops = {
.ioctl = davinci_rtc_ioctl,
.read_time = davinci_rtc_read_time, /* 读取时间 */
.set_time = davinci_rtc_set_time, /* 设置时间 */
.alarm_irq_enable = davinci_rtc_alarm_irq_enable, /* 中断使能 */
.read_alarm = davinci_rtc_read_alarm, /* 读取闹钟时间 */
.set_alarm = davinci_rtc_set_alarm, /* 设置闹钟时间 */
};

在注册cdev过程中,cdev绑定了struct file_operations rtc_dev_fops

static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.unlocked_ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
};

当我们在应用层open("/dev/rtcx")时,它会调用rtc_dev_fops->open():

 static int rtc_dev_open(struct inode *inode, struct file *file)
{
int err;
struct rtc_device *rtc = container_of(inode->i_cdev,
struct rtc_device, char_dev);
const struct rtc_class_ops *ops = rtc->ops;
...
err = ops->open ? ops->open(rtc->dev.parent) : ;
...
return err;
}

open()函数最终会调用struct rtc_class_ops davinci_rtc_ops的open()函数,由于davinci_rtc_ops没有open()函数,在此不做分析。

当我们在应用层open()后,使用ioctl(int fd, unsigned long cmd, ...)时,它会调用rtc_dev_fops->ioctl():

 static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = ;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops; /* 获取rtc_class_ops */
struct rtc_time tm;
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg; err = mutex_lock_interruptible(&rtc->ops_lock);
...
switch (cmd) {
...
case RTC_RD_TIME:
mutex_unlock(&rtc->ops_lock); err = rtc_read_time(rtc, &tm);
if (err < )
return err; if (copy_to_user(uarg, &tm, sizeof(tm)))
err = -EFAULT;
return err;
...
} done:
mutex_unlock(&rtc->ops_lock);
return err;
}

在此以读时间(RTC_RD_TIME)为例,调用关系如下:

rtc_read_time(rtc, &tm);
-> __rtc_read_time(rtc, tm);
-> err = rtc->ops->read_time(rtc->dev.parent, tm) /* davinci_rtc_read_time */
-> tm->tm_sec = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_SEC)); /* 读数据 */
-> day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY0);
-> convertfromdays(days, tm) /* 格式转换 */

通过对两结构体分析,我们可以确定:

1. rtc_device->cdev是对rtc的抽象,用于与应用层进行交互

2. rtc_device->ops是cdev->ops的底层实现,通过读写寄存器完成时间操作

二、修改内核支持RTC

在开发板上执行

# ls /dev/rtc*

会有部分开发板找不到该字符设备,这是因为内核里只定义了rtc平台设备,但是没有注册,所以平台驱动和设备并没有匹配,因此我们需要修改内核里的注册数组或注册平台设备。

对于itop4412,其内核里的注册数组位于arch/arm/mach-exynos/mach-itop4412.c的第2740行。

 static struct platform_device *smdk4x12_devices[] __initdata = {
...
&s3c_device_rtc,
...
};

可以看到,它已经含有了rtc设备。若没有,读者需要根据自身情况进行添加,并重新编译烧写内核。

对于之前使用的dm365.c,它的注册方法是直接在init()函数中注册平台设备:

 static int __init dm365_init_devices(void)
{
if (!cpu_is_davinci_dm365())
return ; davinci_cfg_reg(DM365_INT_EDMA_CC);
platform_device_register(&dm365_edma_device); platform_device_register(&dm365_mdio_device);
platform_device_register(&dm365_emac_device);
clk_add_alias(NULL, dev_name(&dm365_mdio_device.dev),
NULL, &dm365_emac_device.dev); /* Add isif clock alias */
clk_add_alias("master", dm365_isif_dev.name, "vpss_master", NULL);
platform_device_register(&dm365_vpss_device);
platform_device_register(&dm365_isif_dev);
platform_device_register(&vpfe_capture_dev);
return ;
}

可以看到他并没有支持rtc,因此我们可以添加如下代码让开发板支持rtc。

 davinci_cfg_reg(DM365_INT_PRTCSS);              /* 配置管脚复用 */
platform_device_register(&dm365_rtc_device); /* 注册平台设备 */

然后重新编译内核、烧写。

接下来,我们可以来操作rtc了。

在Linux中有两个时钟:硬件时钟(寄存器时钟)和系统时钟(内核时钟)

使用hwclock可以查看硬件时钟,使用date命令可以查看系统时钟。

date命令:

1. 查看系统时间

2. 格式化查看系统时间

%Y:年,%m:月,%d:日,%H:时,%M:分,%S:秒

3. 设置系统时间

格式为:date 月日时分年.秒

hwclock命令:

命令常用参数如下:

-r,--show:读取并打印硬件时钟

-s,--hctosys:将硬件时钟同步到系统时钟

-w,--systohc:将系统时钟同步到硬件时钟

使用方法如下图:

下一章  二十二、DMA驱动

二十一、RTC驱动的更多相关文章

  1. linux RTC 驱动模型分析【转】

    转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...

  2. 无废话ExtJs 入门教程二十一[继承:Extend]

    无废话ExtJs 入门教程二十一[继承:Extend] extjs技术交流,欢迎加群(201926085) 在开发中,我们在使用视图组件时,经常要设置宽度,高度,标题等属性.而这些属性可以通过“继承” ...

  3. Bootstrap <基础二十一>徽章(Badges)

    Bootstrap 徽章(Badges).徽章与标签相似,主要的区别在于徽章的边角更加圆滑. 徽章(Badges)主要用于突出显示新的或未读的项.如需使用徽章,只需要把 <span class= ...

  4. 【圣诞特献】Web 前端开发精华文章推荐【系列二十一】

    <Web 前端开发精华文章推荐>2013年第九期(总第二十一期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和  ...

  5. Citrix 服务器虚拟化之二十一 桌面虚拟化之部署Provisioning Services

    Citrix 服务器虚拟化之二十一  桌面虚拟化之部署Provisioning Services Provisioning Services 是Citrix 出品的一系列虚拟化产品中最核心的一个组件, ...

  6. 二十一、contextMap中放的常用数据

    二十一.contextMap中放的常用数据 request:请求范围的数据.即ServletRequest中的那个Map parameters:请求参数的数据.即request.getParamete ...

  7. 牢记!SQL Server数据库开发的二十一条注意点

    如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL  Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS). ...

  8. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  9. COJ 0979 WZJ的数据结构(负二十一)

    WZJ的数据结构(负二十一) 难度级别:C: 运行时间限制:5000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 请你实现一个数据结构,完成这样的功能: 给你一个 ...

随机推荐

  1. ubuntu之路——day9.2 Covariate shift问题和Batch Norm的解决方案

    Batch Norm的意义:Covariate shift的问题 在传统的机器学习中,我们通常会认为source domain和target domain的分布是一致的,也就是说,训练数据和测试数据是 ...

  2. 第K个幸运数(京东2017秋招真题)

    题目 4和7是两个幸运数字,我们定义,十进制表示中,每一位只有4和7两个数的正整数都是幸运数字.前几个幸运数字为:4,7,44,47,74,77,444,447... 现在输入一个数字K,输出第K个幸 ...

  3. Docker的学习(一)Windows下安装docker环境以及基础的配置

    Docker是什么我这里就不多做介绍了,相信大家都清楚,网上有很多介绍的文章所以作为菜鸟的我就不用我的眼光以及理解来为大家介绍了,还是那句话,这篇文章主要是用作记录学习过程,希望不会误导新人,也希望各 ...

  4. 微信公众平台开发教程Java版(三) 消息接收和发送

    https://www.iteye.com/blog/tuposky-2017429 前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息, ...

  5. 谷歌分析(Google Analytics) 是什么

    谷歌分析(Google Analytics) 是什么 一.总结 一句话总结: 谷歌分析,即大家俗称的ga,全称google analytics,是谷歌推出的网站流量分析工具,可以说是当前业界最强大的流 ...

  6. 数据缺失值的处理 | R包 - mice

    有些情况下缺失值会零星的分布在数据当中,这时去掉所有包含缺失值的样本就不行了,直接用0去填补缺失值也不行. 所以此时就应该用拟合的方法来填补缺失值. library(mice) init = mice ...

  7. 沃顿商学院的MBA课程

    沃顿商学院的MBA课程,分为必修课和选修课两部分 (一)必修课: 1.领导力:团队合作和领导力的基础 2.营销学:营销管理 3.微观经济学:微观经济基础 4.经济学:管理经济学的高级话题 5.统计学: ...

  8. MQTT 客户端应用及常见问题(C#)

    https://blog.csdn.net/dengyaan/article/details/51752327 最近因为工作需要,需要使用C# 语言编写一个通过MQTT协议 ,上传数据到云端的工具.因 ...

  9. 服务端高并发分布式架构 ESB 企业服务总线

    服务端高并发分布式架构演进之路 - 个人文章 - SegmentFault 思否 https://segmentfault.com/a/1190000018626163 ESB 企业服务总线讲解 ht ...

  10. git之删除untrack files

    退回版本 git reset --hard commit_id //不保留未提交的修改 git reset --soft commit_id //默认方式,保留未提交的修改 撤除本地没有提交的修改 g ...