title: RTC子系统

tags: linux

date: 2019/1/2 17:15:27

toc: true

RTC子系统

引入

hctosys.c

查看下内核打印的错误信息如下,很明确指定了程序的入口了

drivers/rtc/hctosys.c: unable to open rtc device (rtc0)

程序流程如下:

// 这个也就是定义了段属性,内核在启动的时候会调用的
late_initcall(rtc_hctosys);
#define late_initcall(fn) __define_initcall("7",fn,7) static int __init rtc_hctosys(void)
{
int err;
struct rtc_time tm;
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); if (rtc == NULL) {
printk("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -ENODEV;
}
....
}

interface.c

搜索下为什么打不开设备rtc_class_open,可以发现是寻找全局变量rtc_class总线的设备

struct rtc_device *rtc_class_open(char *name)
{
struct device *dev;
struct rtc_device *rtc = NULL; down(&rtc_class->sem);
list_for_each_entry(dev, &rtc_class->devices, node) {
....
}

可以看到在drivers\rtc\interface.c中包含了RTC的打开,设置事件设置闹钟等

class.c

搜索这个全局的链表,可以看到在drivers\rtc\class.c中注册的

struct class *rtc_class;
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
static int __init rtc_init(void)
subsys_initcall(rtc_init); #define subsys_initcall(fn) __define_initcall("4",fn,4) #define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn

subsys_initcall其实就是定义一个段属性,内核会主动调用的,查看该文件的入口

static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();
rtc_sysfs_init(rtc_class);
return 0;
}

小结

  • hctosys.c中的入口rtc_hctosys是使用__define_initcall("7",fn,7)

  • class.c的入口rtc_init是使用__define_initcall("4",fn,4)

  • 内核按照先后顺序调用,内核的链接脚本如下

      __initcall_start = .;
    *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
    __initcall_end = .;
  • 搜索这个链接脚本,可以看到

    extern initcall_t __initcall_start[], __initcall_end[];
    static void __init do_initcalls(void)
    {
    for (call = __initcall_start; call < __initcall_end; call++) {
    ...
    }
  • 也就是按找序号调用了,先执行rtc_init,再去打开,想想也是这样的

流程一览

框架分析

rtc_init

drivers\rtc\class.c这是内核入口初始化注册设备,rtc_init()->rtc_dev_init(),来注册字符设备

  • 创建类
  • 分配主次设备号
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc"); rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();
#define RTC_DEV_MAX 16
alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
rtc_sysfs_init(rtc_class);
return 0;
}

rtc_device_register

搜索这个函数,是在drivers\rtc\rtc-s3c.c调用的

rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);

具体形式如下:

  • cdev_init > cdev_add 注册字符设备驱动
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
{
> 设置rtc结构体
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class; //这个是全局的,内核刚开始就注册了的在rtc_init
rtc->dev.release = rtc_device_release; //这里是总线bus 后面会有 device_add 会来匹配,如果没有对应的bus匹配
snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id);
//>dev->bus->probe
//drv->probe //没有dev->bus->probe执行 rtc_dev_prepare(rtc);
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc_dev_add_device(rtc);
cdev_add(&rtc->char_dev, rtc->dev.devt, 1)
// vfs 相关 sysfs 和 proc
rtc_sysfs_add_device
rtc_proc_add_device(rtc); }

s3c_rtc_probe

简述

  • 获得硬件资源,设置寄存器

  • 设置rtc_device结构体,包含了实际的硬件操作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,
    };
  • 注册字符设备驱动,操作函数为rtc_dev_fops,通用接口,最终应该会使用具体的硬件操作接口

    struct rtc_device *rtc_device_register(const char *name, struct device *dev,
    const struct rtc_class_ops *ops,
    struct module *owner)
    {
    struct rtc_device *rtc;
    rtc->ops = ops;
    rtc_dev_prepare(rtc);
    >cdev_init(&rtc->char_dev, &rtc_dev_fops); } //drivers\rtc\rtc-dev.c
    static const struct file_operations rtc_dev_fops = {
    .owner = THIS_MODULE,
    .llseek = no_llseek,
    .read = rtc_dev_read,
    .poll = rtc_dev_poll,
    .ioctl = rtc_dev_ioctl,
    .open = rtc_dev_open,
    .release = rtc_dev_release,
    .fasync = rtc_dev_fasync,
    };

详细

s3c_rtc_probe会调用rtc_device_register来注册rtc设备,这是一个platform平台了,资源文件如下

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,
},
};

搜索资源文件名找到相应设备文件arch\arm\plat-s3c24xx\devs.c 包含了寄存器地址和中断号

struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
}; static struct resource s3c_rtc_resource[] = {
[0] = {
.start = S3C24XX_PA_RTC,
.end = S3C24XX_PA_RTC + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_RTC,
.end = IRQ_RTC,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_TICK,
.end = IRQ_TICK,
.flags = IORESOURCE_IRQ
}
};

具体的函数流程如下:

s3c_rtc_probe
//获得中断号 RQ_TICK节拍和RTC闹钟 ,寄存器等资源
s3c_rtc_tickno = platform_get_irq(pdev, 1);
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//内存分配 寄存器映射
s3c_rtc_mem = request_mem_region(res->start,res->end-res->start+1,pdev->name);
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); //使能RTC,设置寄存器
s3c_rtc_enable(pdev, 1);
//读取RTC寄存器
pr_debug("s3c2410_rtc: RTCCON=%02x\n",readb(s3c_rtc_base + S3C2410_RTCCON));
//设置频率,设置TICONT寄存器,使能节拍中断,设置节拍计数值
s3c_rtc_setfreq(s3c_rtc_freq); //注册驱动
rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
> 设置rtc结构体
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class; //这个是全局的,内核刚开始就注册了的在rtc_init
rtc->dev.release = rtc_device_release; //初始化cdev结构体,绑定file_operations
rtc_dev_prepare(rtc);
//这个应该是唤醒队列了
>init_waitqueue_head(&rtc->irq_queue);
>cdev_init(&rtc->char_dev, &rtc_dev_fops);
// 添加到内核,这里好像是udev机制
device_register(&rtc->dev); //添加到驱动设备
rtc_dev_add_device(rtc);
> cdev_add
// /sysfs/文件
rtc_sysfs_add_device(rtc);
> device_create_file(&rtc->dev, &dev_attr_wakealarm);
//创建 /proc/下的一些东西
rtc_proc_add_device(rtc);
>create_proc_entry("driver/rtc", 0, NULL)
>ent->proc_fops = &rtc_proc_fops;

open

公用

我们注册驱动的时候是注册了rtc_dev_fops,它是一个公共的,具体如何找到实际硬件的open?

static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
}; static int rtc_dev_open(struct inode *inode, struct file *file)
{
int err;
//获取对应的rtc_device
struct rtc_device *rtc = container_of(inode->i_cdev,
struct rtc_device, char_dev);
//这里就能得到实际的ops了
const struct rtc_class_ops *ops = rtc->ops; //这里就调用实际的open
err = ops->open ? ops->open(rtc->dev.parent) : 0;
}

芯片级

申请了闹钟中断和tick中断

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,
}; static int s3c_rtc_open(struct device *dev)
{
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;
}

ioctl

同样的,公共级别的ioctl也会调用到芯片级的

static int rtc_dev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg);
...
}

芯片级

这里也就是读写时间,操作寄存器了

static int s3c_rtc_ioctl(struct device *dev,
unsigned int cmd, unsigned long arg)
{
unsigned int ret = -ENOIOCTLCMD; switch (cmd) {
case RTC_AIE_OFF:
case RTC_AIE_ON:
s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
ret = 0;
break; case RTC_PIE_OFF:
case RTC_PIE_ON:
tick_count = 0;
s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
ret = 0;
break; case RTC_IRQP_READ:
ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg);
break; case RTC_IRQP_SET:
/* check for power of 2 */ if ((arg & (arg-1)) != 0 || arg < 1) {
ret = -EINVAL;
goto exit;
} pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); s3c_rtc_setfreq(arg);
ret = 0;
break; case RTC_UIE_ON:
case RTC_UIE_OFF:
ret = -EINVAL;
} exit:
return ret;
}

加入时钟

可以看到内核是有驱动的,只是没有注册平台设备文件 ,添加这个设备文件

ls /dev/rtc*

arch/arm/plat-s3c24xx/Common-smdk.c加入s3c_device_rtc这个结构即可

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

测试

上电启动信息如下

s3c2410-rtc s3c2410-rtc: setting the system clock to 2165-10-04 10:44:26 (1882584970)

查看下设备

# ls /dev/rtc*
/dev/rtc0

date命令

使用date命令读时间

# 查看时间
# date
Tue Aug 28 04:17:30 UTC 2029
# date "+ %Y/%m/%d %H:%M:%S"
2029/08/28 04:18:06

使用date设置时间,格式是date 月日时分年.秒

# date 010314322019.30
Thu Jan 3 14:32:30 UTC 2019 # date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:04
# date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:10
# date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:11

hwclock命令

  -r, --show          读取并打印硬件时钟(read hardware clock and print result )
-s, --hctosys 将硬件时钟同步到系统时钟(set the system time from the hardware clock )
-w, --systohc 将系统时钟同步到硬件时钟(set the hardware clock to the current system time )

使用如下

#读取硬件时钟
# hwclock -r
Wed Dec 31 23:59:59 1969 0.000000 seconds
# 同步,设置软时钟到硬件时钟
# hwclock -w
# hwclock -r
Thu Jan 3 14:35:49 2019 0.000000 seconds

RTC子系统的更多相关文章

  1. Linux驱动修炼之道-RTC子系统框架与源码分析【转】

    转自:http://helloyesyes.iteye.com/blog/1072433 努力成为linux kernel hacker的人李万鹏原创作品,为梦而战.转载请标明出处 http://bl ...

  2. linux驱动基础系列--linux rtc子系统

    前言 linux驱动子系统太多了,连时钟也搞了个子系统,这导致一般的时钟芯片的驱动也会涉及到至少2个子系统,一个是时钟芯片接口子系统(比如I2c接口的时钟芯片),一个是内核给所有时钟芯片提供的rtc子 ...

  3. Linux下的RTC子系统

    转自:http://blog.csdn.net/weiqing1981127/article/details/8484268 实时时钟的作用主要是为操作系统提供一个可靠的时间,并在断电下,RTC时钟也 ...

  4. 初探linux子系统集之timer子系统(一)

    一般来说要让整个linux系统跑起来,那么一个必须的就是linux的时钟,也就是时间子系统了,这里正好工作需要,那么就研究下linux下的时间子系统了. linux内核必须完成两种主要的定时测量.一个 ...

  5. linux内核中有哪些子系统(框架)呢?

    注意: 分析用的linux内核版本为5.1.3 1. RTC子系统 2. Remote Processor子系统 3. Remote Processor Message子系统 4. SCSI子系统 5 ...

  6. 30.Linux-RTC驱动分析及使用

    linux中的rtc驱动位于drivers/rtc下,里面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以它的RTC驱动为rtc-s3c.c 1.进入./drivers/rtc/r ...

  7. 【ARM-Linux开发】【DSP开发】AM5728介绍

    AM5728 Sitara Processors 1.    介绍 1.1 AM572x概述 AM572x是高性能,Sitara器件.以28nm技术集成: 结构设计主要考虑嵌入式应用,包括工业通讯,人 ...

  8. linux设备驱动的分层设计思想--input子系统及RTC

    转自:linux设备驱动的分层设计思想 宋宝华 http://blog.csdn.net/21cnbao/article/details/5615493 1.1 设备驱动核心层和例化 在面向对象的程序 ...

  9. Linux时间子系统之(四):timekeeping

    专题文档汇总目录 Notes:timekeeping模块的狠心数据结构是timekeeper,它维护了系统不同类型时钟的时间值,并且介绍了获取不同类型时钟时间的函数. clocksource切换通过c ...

随机推荐

  1. linux内核数据结构之kfifo【转】

    1.前言 最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的.缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度.例如一个进程A产 ...

  2. Filebeat插件启动失败,不能直接查找报错原因

    老是在filebeat启动的这一步骤上出错,但是由于filebeat是由systemd启动的,因此原因也经常查不清楚,因此并不能直观的查出错误在哪里,所以今天教给大家两个寻找错误的根源的方法 先看我这 ...

  3. LeetCode算法题-Max Consecutive Ones(Java实现)

    这是悦乐书的第242次更新,第255篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第109题(顺位题号是485).给定二进制数组,找到此数组中连续1的最大数量.例如: 输 ...

  4. css3新特性合集

    转自:https://www.cnblogs.com/xiaoxie2016/p/5964694.html (若原作者对此转载有疑问,联系删除,谢谢!) animation    IE10 anima ...

  5. yidiandian

    hzwer libreoj (需要拿新版的打开)

  6. Mac系统下Mysql存储数据报错 ER_TRUNCATED_WRONG_VALUE_FOR_FIELD: Incorrect string value

    比如如下mysql操作插入数据: const mysql = require('mysql'); /* createConnection方法创建一个表示与Mysql数据库服务器之间连接的 Connec ...

  7. NetSec2019 20165327 Exp7 网络欺诈防范

    NetSec2019 Exp7 网络欺诈防范 一.本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体实践有 (1)简单应用SET工具建立冒名网站 (1分) (2)ette ...

  8. 如何解决一个从SkylineGlobe5版本升级到7版本遇到的小问题

    前些天,有朋友问,用Skyline5版本开发的WinForm程序,升级到7版本的时候,工程提示下面这样“创建组件AxHost失败”的错误,该如何解决呢? 后来经过百度搜索,找到了这样的答案, 测试发现 ...

  9. 控制结构(10): 指令序列(opcode)

    // 上一篇:管道(pipeline) // 下一篇:Continuation-passing_style(CPS) 发现问题 在一个正式项目的开发周期中,除了源代码版本控制外,还存在着项目的配置/编 ...

  10. python 支付宝SDK

    python 支付宝SDK代码如下 from datetime import datetime from Crypto.PublicKey import RSA from Crypto.Signatu ...