一、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. addEventListener与attachEvent区别

    DOM2级事件处理程序 DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作: addEventListener removeEventListener 所有的DOM节点都包含这两个方法, ...

  2. ubuntu之路——day6(今天对数据集的建立有了更深的体会)

    两个重点: 一.举个例子,如果建立一个图像识别的数据集,你的训练集和你的训练验证集是从网上爬下来的(也就是说这些图片的大小.像素.后期制作都可能很精美),你真正的测试集是用户的手机上传(不同的手机.环 ...

  3. 三大框架 之 Struts2

    目录 Struts2 Struts2简介 Struts2框架的作用 常见web层的框架 web框架特点 Struts2基本使用 Struts2执行流程 Struts2配置 struts2的加载顺序 P ...

  4. 三大框架 之 Hibernate查询(一对多、多对多、查询关系)

    目录 一对多 表之间关系 表之间关系建表原则 一对多关系配置 建立表 建立ORM 添加配置文件 在hibernate.cfg.xml中的标签里,添加核心配置文件 引入工具类 编写测试类 级联操作 什么 ...

  5. futex的设计与实现

    介绍 futex(快速用户空间互斥)是Linux的一个基础组件,可以用来构建各种更高级别的同步机制,比如锁或者信号量等等,POSIX信号量就是基于futex构建的.大多数时候编写应用程序并不需要直接使 ...

  6. Tosca:设置执行结束时间

  7. (E2E_L2)包含目录、库目录、附加包含目录、附加库目录、附加依赖项之详解【正确的目录添加方法】

    VS项目中的包含目录.库目录.附加包含目录.附加库目录.附加依赖项均在"项目->属性->配置属性"下进行配置 一.具体说明如下: 1.VC++目录:     包含目录: ...

  8. AI学习网址记录

    https://ai.yanxishe.com/ https://ai.yanxishe.com/page/blogDetail/14365 GAN网络 对抗式生成网络-图像超分辨及图像修复 完全可见 ...

  9. Linux 服务器远程管理

    一.Linux 常用远程管理工具 点击下载 二.查看服务器 ip 地址命令 1.通过 ip addr 查看网卡 ip 地址 ip addr 2.通过 ifconfig 查看网卡 ip 地址 最小化安装 ...

  10. java8 数据集过滤removeIf和filter

    对象如下,需求:只要30岁以下的人 //求职者的实体类 public class Person { private String name;//姓名 private Integer age;//年龄 ...