RK3288_Android7.1调试RTC总结(一)主要是讲解rtc的调试和驱动框架浅析,
RK3288_Android7.1调试RTC总结(二)主要讲解看门狗方面的内容。

一、 Linux时间有两个,系统时间(Wall Time)和RTC时间。
每次Linux系统启动后在启动过程中会检测和挂载RTC驱动,在挂载后会自动从RTC芯片中读取时间并设置到系统时间中去。此后如果没有显式的通过命令去控制RTC的读写操作,系统将不会再从RTC中去获取或者同步设置时间。

(1)系统时间(WT):由Linux系统软件维持的时间。系统时间是由主芯片的定时器进行维护的时间,一般情况下都会选择芯片上最高精度的定时器作为系统时间的定时基准,以避免在系统运行较长时间后出现大的时间偏移。特点是掉电后不保存。通过command: date 获取到的就是系统时间。

rk3288:/ # date
Thu Jan  1 00:02:39 GMT 1970

(2)RTC时间:这个时间来自我们设备上的RTC芯片。RTC芯片都有电池+系统电源的双重供电机制,在系统正常工作时由系统供电,在系统掉电后由电池进行供电。因此系统电源掉电后RTC时间仍然能够正常运行。通过command: hwclock 可以读取RTC时间。

rk3288:/ # hwclock
[  235.534023] rtc_am1805_open :open success!
[  235.534059] rtc_am1805_read_tiMon Mar  7 00:03:55 1983  0.000000 seconds

其他命令汇总:

date  //查看系统时间
date 011209512019.01  //设置系统时间,月/日/时/分/年/.秒,这里就是设置为01月12日09点51分2019年01秒
hwclock --show  //查看硬件时间(也就是rtc时间)
hwclock --hctosys或者# clock --hctosys  //硬件时钟与系统时钟同步,(hc代表硬件时间,sys代表系统时间)
hwclock --systohc或者# clock --systohc  //系统时钟和硬件时钟同步

二、WT时间(系统时间)和RTC时间
当WT时间来自于RTC时间时,流程是:

机器上电–>RTC驱动加载–>从RTC同步时间到WT时间

代码部分(从RTC同步时间到WT时间):

文件:drivers\rtc\hctosys.c

 27 static int __init rtc_hctosys(void)28 {29         int err = -ENODEV;30         struct rtc_time tm;31         struct timespec64 tv64 = {32                 .tv_nsec = NSEC_PER_SEC >> 1,33         };34         struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);3536         if (rtc == NULL) {37                 pr_info("unable to open rtc device (%s)\n",38                         CONFIG_RTC_HCTOSYS_DEVICE);39                 goto err_open;40         }4142         err = rtc_read_time(rtc, &tm);43         if (err) {44                 dev_err(rtc->dev.parent,45                         "hctosys: unable to read the hardware clock\n");46                 goto err_read;4748         }4950         tv64.tv_sec = rtc_tm_to_time64(&tm);5152         err = do_settimeofday64(&tv64);5354         dev_info(rtc->dev.parent,55                 "setting system clock to "56                 "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",57                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,58                 tm.tm_hour, tm.tm_min, tm.tm_sec,59                 (long long) tv64.tv_sec);6061 err_read:62         rtc_class_close(rtc);6364 err_open:65         rtc_hctosys_ret = err;6667         return err;68 }6970 late_initcall(rtc_hctosys);

late_initcall说明系统在启动流程的后面才会调用该函数去同步时间。

Hardware:提供时间信息(time&alarm),通过一定的接口和RTC driver (rtc_am1805.c):进行交互。Driver完成硬件的访问功能,提供访问硬件接口,以驱动的形式驻留在系统。注册方式由class.c:文件提供。驱动注册成功后会构建rtc_device结构体表征的rtc设备,并把rtc芯片的操作方式存放到rtc设备的ops成员中。

interface.c //文件屏蔽硬件相关的细节,向上提供统一的获取/设置时间或Alarm的接口;
rtc-lib.c //文件提供通用的时间操作函数,如rtc_time_to_tm、rtc_valid_tm等;
rtc-dev.c //文件在/dev/目录下创建设备节点供应用层访问,如open、read、ioctl等,访问方式填充到file_operations结构体中;
hctosys.c/rtc-sys.c/rtc-proc.c //看名字就知道其作用;
hctosys.c //设置硬件(rtc chip)的时间到系统;
systohc.c //设置系统的时间到硬件(rtc chip);

下面一层一层分析驱动框架:
三、RTC驱动(我这里以 am 1805驱动 为例)

驱动主要工作是填充rtc_class_ops结构体,结构体描述了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 *);int (*set_time)(struct device *, struct rtc_time *);int (*read_alarm)(struct device *, struct rtc_wkalrm *);int (*set_alarm)(struct device *, struct rtc_wkalrm *);int (*proc)(struct device *, struct seq_file *);int (*set_mmss)(struct device *, unsigned long secs);int (*read_callback)(struct device *, int data);int (*alarm_irq_enable)(struct device *, unsigned int enabled);};

具体实现:

 static const struct rtc_class_ops rtc_am1805_ops = {.open       = rtc_am1805_open,.read_time = rtc_am1805_read_time,.set_time = rtc_am1805_write_time,.read_alarm = rtc_am1805_read_alarm,.set_alarm = rtc_am1805_set_alarm,.set_cdt_time = rtc_am1805_set_timer,.read_cdt_time = rtc_am1805_read_timer,.ioctl      = rtc_am1805_ioctl,};

注册设备:

rtc_device_register(name, dev, &test_rtc_ops, THIS_MODULE);

驱动加载成功log(这里时间是不对的,这log只是说明加载rtc驱动成功,生成rtc设备rtc0):

 rk3288:/ $ dmesg | grep 1805                                                   [    0.841976] rtc_am1805 --rtc_am1805_init[    0.858671] rtc_am1805_get_chip_id :read chip id reg:18.[    0.859867] ___ read from am1805:  c139-4c-c0 00:05:32[    0.860057] rtc_am1805_read_time [    0.860893] ___ read from am1805:  c139-4c-df 00:05:32[    0.860919] rtc_am1805 read time 12139-11-25 00:05:32[    0.860928] rtc_am1805_read_alarm [    0.863389] rtc_am1805_read_time [    0.864215] ___ read from am1805:  c139-4c-df 00:05:32[    0.864239] rtc_am1805 read time 12139-11-25 00:05:32[    0.864425] rtc_am1805 1-0069: rtc core: registered rtc_am1805 as rtc0  //rtc设备rtc0[    0.864487] rtc_am1805 --rtc_am1805_probe ok[    0.866065] rtc_am1805_probe : ret is 1.[    2.117404] rtc_am1805_read_time [    2.118521] ___ read from am1805:  c139-4c-df 00:05:34[    2.118690] rtc_am1805 read time 12139-11-25 00:05:34[    2.118795] rtc_am1805 1-0069: setting system clock to 14039-12-25 00:05:34 UTC (380891808334)

四、RTC驱动注册
文件:./drivers/rtc/class.c

 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();rtc_sysfs_init(rtc_class);return 0;}subsys_initcall(rtc_init);

完成:
1、 创建名为rtc的class
2、 提供休眠唤醒相关接口suspend/resume
3、 rtc_dev_init():动态申请/dev/rtcN的设备号
4、 rtc_sysfs_init():rtc类具有的device_attribute属性

接着看rtc驱动注册:

 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;struct rtc_wkalrm alrm;int id, err;//Linux支持多个RTC设备,所以需要为每一个设备分配一个ID// 对应与/dev/rtc0,/dev/rtc1,/dev/rtcNid = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);//创建rtc_device设备(对象)并初始化rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);rtc->id = id;rtc->ops = ops; // 2.1 对应RTC驱动填充的test_rtc_opsrtc->owner = owner;rtc->irq_freq = 1;rtc->max_user_freq = 64;rtc->dev.parent = dev;rtc->dev.class = rtc_class;//rtc_init()创建的rtc_classrtc->dev.release = rtc_device_release;//rtc设备中相关锁、等待队列的初始化mutex_init(&rtc->ops_lock);spin_lock_init(&rtc->irq_lock);spin_lock_init(&rtc->irq_task_lock);init_waitqueue_head(&rtc->irq_queue);//初始化工作队列rtc_timer_do_worktimerqueue_init_head(&rtc->timerqueue);INIT_WORK(&rtc->irqwork, rtc_timer_do_work);//初始化RTC闹钟中断rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);//RTC更新中断rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);//RTC周期性中断hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);rtc->pie_timer.function = rtc_pie_update_irq;rtc->pie_enabled = 0;//检查是否设置闹钟err = __rtc_read_alarm(rtc, &alrm);//如果RTC芯片中设置了有效的Alarm,则初始化:加入到rtc->timerqueue队列中if (!err && !rtc_valid_tm(&alrm.time))rtc_initialize_alarm(rtc, &alrm);//根据name参数设置rtc的name域strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);//设置rtc的dev成员中的name域dev_set_name(&rtc->dev, "rtc%d", id);// /dev/rtc0的rtc作为字符设备进行初始化// rtc_dev_prepare-->cdev_init(&rtc->char_dev, &rtc_dev_fops);rtc_dev_prepare(rtc);//注册rtc设备到系统err = device_register(&rtc->dev);//rtc设备作为字符设备添加到系统// rtc_dev_add_devicec-->dev_add(&rtc->char_dev, rtc->dev.devt, 1)// 然后就存在/dev/rtc0了rtc_dev_add_device(rtc);rtc_sysfs_add_device(rtc);// 9、/proc/rtcrtc_proc_add_device(rtc);dev_info(dev, "rtc core: registered %s as %s\n",rtc->name, dev_name(&rtc->dev));return rtc;
}

有了/dev/rtc0后,应用层就可以通过open/read/ioctl操作RTC设备了,对应与内核的file_operations:

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

五、硬件抽象层

硬件抽象,即屏蔽具体的硬件细节,为上层用户提供统一的调用接口,使用者无需关心这些接口是怎么实现的。以RTC访问为例,抽象的实现位于interface.c文件,其实现基于class.c中创建的rtc_device设备。

实现原理,以rtc_set_time为例:

 int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm){int err;//参数检测err = rtc_valid_tm(tm);err = mutex_lock_interruptible(&rtc->ops_lock);if (err)return err;//调用rtc_device中ops结构体的函数指针// ops结构体的函数指针已经在RTC驱动中被赋值if (!rtc->ops)err = -ENODEV;else if (rtc->ops->set_time)err = rtc->ops->set_time(rtc->dev.parent, tm);mutex_unlock(&rtc->ops_lock);/* A timer might have just expired */schedule_work(&rtc->irqwork);return err;}

六、rtc文件系统
1、 rtc在sysfs之前曾建立过名为rtc的class:

rtc_class = class_create(THIS_MODULE, "rtc");

查看rtc class:

 rk3288:/ $ ls /sys/class/rtc/ rtc0rk3288:/ $ ls -l /sys/class/rtc/total 0lrwxrwxrwx 1 root root 0 1970-01-01 00:00 rtc0 -> ../../devices/platform/ff140000.i2c/i2c-1/1-0069/rtc/rtc0rk3288:/ $

我们系统中只有一个RTC,所以编号为rtc0。同时发现rtc0文件为指向/sys/devices/ff140000.i2c/i2c-1/1-0069/rtc/rtc0的符号链接,RTC芯片是I2C接口,所以rtc0挂载在I2C的总线上,总线控制器地址ff140000,控制器编号为1,RTC芯片作为slave端地址为0x69。

rtc0设备属性:
文件:./drivers/rtc/rtc-sysfs.c

214 static struct attribute *rtc_attrs[] = {215         &dev_attr_name.attr,
216         &dev_attr_date.attr,
217         &dev_attr_time.attr,
218         &dev_attr_since_epoch.attr,
219         &dev_attr_max_user_freq.attr,
220         &dev_attr_hctosys.attr,
221         &dev_attr_wakealarm.attr,
222         NULL,
223 };

查看下rtc0的属性:

 rk3288:/ $ ls -l /sys/class/rtc/rtc0/total 0-r--r--r-- 1 root root 4096 1970-01-01 00:16 date-r--r--r-- 1 root root 4096 1970-01-01 00:16 devlrwxrwxrwx 1 root root    0 1970-01-01 00:16 device -> ../../../1-0069-r--r--r-- 1 root root 4096 1970-01-01 00:00 hctosys-rw-r--r-- 1 root root 4096 1970-01-01 00:16 max_user_freq-r--r--r-- 1 root root 4096 1970-01-01 00:16 namedrwxr-xr-x 2 root root    0 1970-01-01 00:00 power-r--r--r-- 1 root root 4096 1970-01-01 00:16 since_epochlrwxrwxrwx 1 root root    0 1970-01-01 00:16 subsystem -> ../../../../../../../class/rtc-r--r--r-- 1 root root 4096 1970-01-01 00:16 time-rw-r--r-- 1 root root 4096 1970-01-01 00:00 uevent-rw-r--r-- 1 root root 4096 1970-01-01 00:16 wakealarm

2、 rtc在proc之前rtc0设备加入到了/proc

 rtc_device_register--->rtc_proc_add_device(rtc);void rtc_proc_add_device(struct rtc_device *rtc){if (is_rtc_hctosys(rtc))proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);}

查看下:

 rk3288:/ $ cat /proc/driver/rtc [ 1067.757881] rtc_am1805_read_time rtc_time        : 00:23:19rtc_date        : 2019-12-23alrm_time       : 00:00:00alrm_date       : 1970-01-01alarm_IRQ       : noalrm_pending    : noupdate IRQ enabled      : noperiodic IRQ enabled    : noperiodic IRQ frequency  : 1max user IRQ frequency  : 6424hr            : yesrk3288:/ $ [ 1067.759019] ___ read from am1805:  c139-4c-dd 00:23:19[ 1067.759135] ___ read to tm format: 2019-11-23 00:23:19[ 1067.759201] rtc_am1805 read time 2019-11-23 00:23:19

信息来源:

 rtc_proc_fops-->rtc_proc_open-->rtc_proc_show

关于late_initcall;kernel中__init类型函数都位于.init.text段中,对应的在.initcall.init段中保存相应的函数指针。系统在启动过程中,根据定义在段中的等级值(0~7)从低到高依次执行。定义:

 init.h (include\linux)#define pure_initcall(fn)        __define_initcall(fn, 0)#define core_initcall(fn)        __define_initcall(fn, 1)#define core_initcall_sync(fn)        __define_initcall(fn, 1s)#define postcore_initcall(fn)        __define_initcall(fn, 2)#define postcore_initcall_sync(fn)    __define_initcall(fn, 2s)#define arch_initcall(fn)        __define_initcall(fn, 3)#define arch_initcall_sync(fn)        __define_initcall(fn, 3s)#define subsys_initcall(fn)        __define_initcall(fn, 4)#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)#define fs_initcall(fn)            __define_initcall(fn, 5)#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)#define rootfs_initcall(fn)        __define_initcall(fn, rootfs)#define device_initcall(fn)        __define_initcall(fn, 6)#define device_initcall_sync(fn)    __define_initcall(fn, 6s)#define late_initcall(fn)        __define_initcall(fn, 7)#define late_initcall_sync(fn)        __define_initcall(fn, 7s)

RK3288_Android7.1调试RTC总结(一)相关推荐

  1. 第十三届蓝桥杯嵌入式备赛-STM32G431-进阶模块调试⑦RTC时钟

    目录 0.前言 1.功能要求 2.STM32Cubemx初始化 3.程序实现 main.c相关初始化 RTC函数 RTC_CalendarGet()日期时间获取函数 设置日期RTC_SetDate和时 ...

  2. Android 恢复出厂设置时间重置

    时间由rtc硬件模块来进行维护的,时间更新后会将时间信息写入此硬件模块,在系统启动时,RTC硬件驱动会读取此值进行设置. 关于android的时间 网上资料了解到,android Linux都有两个时 ...

  3. RK3568开发笔记-iSL1208 RTC时钟芯片调试记录

    文章目录 目录 文章目录 前言 一.RTC部分原理图 二.设备树配置 三.内核配置 四.设备信息查看 五.RTC时间读写设置 总结 前言 实时时钟的缩写是RTC(Real_Time Clock).RT ...

  4. stm32 hal库 rtc 备份 寄存器 备份sram 调试笔记

    rtc 是可以单独供电的,通常设计rtc是带有一个纽扣电池用来给rtc供电.本次是调试rtc 的备份寄存器和备份sram,这两个都是可以在rtc不断电情况下一直保存数据. 步骤 配置rtc参数,用cu ...

  5. 网卡5790c linux驱动,Linux内核配置(12)

    --- Real Time Clock [] Set system time from RTC on startup and resume 系统启动时使用从指定的RTC设备中读取的时间来设定系统时间, ...

  6. RK3588 rtc-hym8563设备开发

    文章目录 前言 一.rockchip_defconfig配置 二.hym8563 dts配置 三.初始化报错处理方法 总结 前言 在调试rtc过程中发现rtc断电开机初始化会出错,但是reboot开机 ...

  7. STM32F429 KEIL 使用笔记(备忘录):代码下出现红色波浪线问题等

    事由:KEIL5使用过程中代码下出现红色波浪线 原因:使用了码折叠功能 解决:取消代码折叠功能 右键->倒数第二的Outlining->Hind All Outlining 关于:代码折叠 ...

  8. 嵌入式开发——rtc时钟调试笔记

    前段时间在调试AM335X的rtc时钟功能,一开始是使用芯片内部的rtc时钟模块,后来发现功耗有点高,转而使用rtc时钟芯片,型号为ds1672..现在把两者的调试要点记录下来,方便后续的查阅. 注: ...

  9. 调试大普RTC芯片驱动-ins5699s

    目录 一.修改设备树,注意设备地址的计算 二.大致的调试步骤 1.检查i2c适配器是否加载成功 2.加载设备驱动,看能否检测到设备 一.修改设备树,注意设备地址的计算 这款时钟芯片是I2C接口,驱动源 ...

  10. RK3399驱动开发 | 15 - RTC实时时钟芯片HYM8563S调试(基于linux5.4.32内核)

    文章目录 一.Linux RTC设备驱动框架 二.HYM8563实时时钟芯片 1. 简介 2. 引脚图 3. 连接原理图 三.设备驱动调试 1. 设备树节点描述 2. 使能内核驱动 3. 测试 四.h ...

最新文章

  1. java 读取txt,java读取大文件
  2. aws sqs_在Spring使用AWS SQS创建消息驱动Bean
  3. foreach判断最后一个_ArrayList集合为什么不能使用foreach增删改?
  4. @RequestBody、@ResponseBody的具体用法和使用时机
  5. go1.14基于信号的抢占式调度实现原理
  6. 766. 托普利茨矩阵
  7. 解决IE浏览器URL乱码的问题
  8. Android反编译分析工具
  9. 论文笔记_S2D.34-2015-CVPR_从单张图像进行深度估计的深度卷积神经场
  10. php中获取不到当前元素,PHP 中数组获取不到元素
  11. 查看MD04结果的程序 Production Planning - Extract data from MRP Table
  12. 【Aspose-CAD for Java】DWG如何优雅的转换成PDF文档!
  13. 计算机随机抽样的方法有,卫生统计学四种随机抽样方法
  14. 离散数学2:命题逻辑的推理
  15. 上海十大it外包公司
  16. matlab非参数检验,非参数检验及matlab实现
  17. Python春节特训营09:老师的点名神器
  18. 什么是 GC,有什么作用?
  19. 推荐一个好用的搜索微信公众号文章的搜索引擎
  20. 山东农业大学/基础训练5

热门文章

  1. 清北学堂模拟day4 捡金币
  2. Extjs中引入JSP页面
  3. Android自定义View研究(七)--XML中布局自定义View时View触摸原点问题
  4. PHP文件系统-文件的读写操作
  5. 基于.net技术的 Rss 订阅开发
  6. 关于DataSet与Strongly typed DataSet几点思考(原创)
  7. HTML(Hepertext Markup Language 超文本标记语言)
  8. 推荐算法竞赛TOP解决方案汇总
  9. 【Linux】常用命令之 awk 常用实例
  10. CNN_原理以及pytorch多分类实践