linux rtc 开发指南 1 概述 1.1 编写目的 介绍linux 内核中rtc 驱动的适配和debug 方法,为rtc 设备的使用者和维护者提供参考。
1.2 适用范围 内核版本 驱动文件
linux-4.9 及以上 rtc-sunxi.c
1.3 相关人员 rtc 驱动及应用层的开发/维护人员。
2 模块介绍 linux 内核中,rtc 驱动的结构图如下所示, 可以分为三个层次:
接口层,负责向用户空间提供操作的结点以及相关接口。 • rtc core, 为rtc 驱动提供了一套api, 完成设备和驱动的注册等。 • rtc 驱动层,负责具体的rtc 驱动实现,如设置时间、闹钟等设置寄存器的操作。
2.2 相关术语介绍 术语 解释说明
sunxi 指allwinner 的一系列soc 硬件平台
rtc real time clock,实时时钟
2.3 源码结构介绍 linux-4.9
└-- drivers
└-- rtc
|-- class.c
|-- hctosys.c
|-- interface.c
|-- rtc-dev.c
|-- rtc-lib.c
|-- rtc-proc.c
|-- rtc-sysfs.c
|-- systohc.c
|-- rtc-core.h
|-- rtc-sunxi.c
└-- rtc-sunxi.h
linux-5.4
└-- drivers
└-- rtc
|-- class.c
|-- hctosys.c
|-- interface.c
|-- dev.c
|-- lib.c
|-- proc.c
|-- sysfs.c
|-- systohc.c
|-- rtc-core.h
|-- rtc-sunxi.c
└-- rtc-sunxi.h
3 模块配置介绍 3.1 kernel menuconfig 配置 3.1.1 linux-4.9 版本下 在命令行中进入内核根目录(kernel/linux-4.9),执行make arch=arm64(arm) menuconfig(32 位系统为make arch=arm menuconfig) 进入配置主界面(linux-5.4 内核版本在longan 目录下执行:./build.sh menuconfig 进入配置主界面),并按以下步骤操作: 首先,选择device drivers 选项进入下一级配置,如下图所示:
选择real time clock,进入下级配置,如下图所示:
选择allwinner sunxi rtc,如下图所示:
由于在关机过程中,rtc 一般都是独立供电的,因此在rtc 电源域中的寄存器不会掉电且rtc寄存器的值也不会恢复为默认值。利用此特性,sunxi 平台支持reboot 命令的一些扩展功能和 假关机功能,但需要打开support ir fake poweroff 和sunxi rtc reboot feature 选项,rtc驱动才能支持这些扩展功能。
3.1.2 linux-5.4 版本下 在命令行中进入longan 顶层目录,执行./build.sh config,按照提示配置平台、板型等信息(如果之前已经配置过,可跳过此步骤)。 然后执行./build.sh menuconfig,进入内核图形化配置界面,并按以下步骤操作: 选择device driver选项进入下一级配置,如下图所示:
选择real time clock进入下一级配置,如下图所示:
选择allwinner sunxi rtc配置,如下图所示。
由于在关机过程中,rtc 一般都是独立供电的,因此在rtc 电源域中的寄存器不会掉电且rtc寄存器的值也不会恢复为默认值。利用此特性,sunxi 平台支持reboot 命令的一些扩展功能,但需要打开sunxi rtc reboot flag和sunxi rtc general register save bootcount选项,rtc 驱动才能支持这些扩展功能。
3.2 device tree 源码结构和路径 soc 级设备树文件(sun*.dtsi)是针对该soc 所有方案的通用配置:
• 对于arm64 cpu 而言,soc 级设备树的路径为:arch/arm64/boot/dts/sunxi/sun*.dtsi
• 对于arm32 cpu 而言,soc 级设备树的路径为:arch/arm/boot/dts/sun*.dtsi
板级设备树文件(board.dts)是针对该板型的专用配置:
• 板级设备树路径:device/config/chips/{ic}/configs/{board}/board.dts
板级设备树文件(board.dts)是针对该板型的专用配置: • 板级设备树路径:device/config/chips/{ic}/configs/{board}/board.dts
3.2.1 linux-4.9 版本下 device tree 的源码结构关系如下:
board.dts
└--------sun*.dtsi
|------sun*-pinctrl.dtsi
└------sun*-clk.dtsi
3.2.2 linux-5.4 版本下 device tree 的源码结构关系如下:
board.dts
└--------sun*.dtsi
3.3 device tree 对rtc 控制器的通用配置 3.3.1 linux-4.9 版本下 1 / {
2 rtc: rtc@07000000 {
3 compatible = allwinner,sunxi-rtc; //用于probe驱动
4 device_type = rtc;
5 auto_switch; //支持rtc使用的32k时钟源硬件自动切换
6 wakeup-source; //表示rtc是具备休眠唤醒能力的中断唤醒源
7 reg = ; //rtc寄存器基地址和映射范围
8 interrupts = ; //rtc硬件中断号
9 gpr_offset = ; //rtc通用寄存器的偏移
10 gpr_len = ; //rtc通用寄存器的个数
11 gpr_cur_pos = ;
12 };
13 }
说明 对于linux-4.9 内核,当rtc 结点下配置auto_switch 属性时,rtc 硬件会自动扫描检查外部32k 晶体振荡器的起振情 况。当外部晶体振荡器工作异常时,rtc 硬件会自动切换到内部rc16m 时钟分频出来的32k 时钟,从而保证rtc 工作正 常。当没有配置该属性时,驱动代码中直接把rtc 时钟源设置为外部32k 晶体的,当外部32k 晶体工作异常时,rtc 会工 作异常。因此建议配置上该属性。
3.3.2 linux-5.4 版本下 1 / {
2 rtc: rtc@7000000 {
3 compatible = allwinner,sun50iw10p1-rtc; //用于probe驱动
4 device_type = rtc;
5 wakeup-source; //表示rtc是具备休眠唤醒能力的中断唤醒源
6 reg = ; //rtc寄存器基地址和映射范围
7 interrupts = ; //rtc硬件中断号
8 clocks = , ; //rtc所用到的时钟
9 clock-names = r-ahb-rtc, rtc-1k; //上述时钟的名字
10 resets = ;
11 gpr_cur_pos = ; //当前被用作reboot-flag的通用寄存器的序号
12 };
13 }
在device tree 中对每一个rtc 控制器进行配置, 一个rtc 控制器对应一个rtc 节点, 节点属性的含义见注释。
3.4 board.dts 板级配置 board.dts用于保存每个板级平台的设备信息(如demo 板、demo2.0 板等等)。board.dts路径如下:
device/config/chips/{ic}/configs/{board}/boar d.dts
在board.dts中的配置信息如果在*.dtsi(如sun50iw9p1.dtsi等) 中存在,则会存在以下覆盖规则:
在board.dts中的配置信息如果在*.dtsi(如sun50iw9p1.dtsi等) 中存在,则会存在以下覆盖规则:
相同属性和结点,board.dts的配置信息会覆盖*.dtsi中的配置信息
新增加的属性和结点,会添加到编译生成的dtb 文件中
4 接口描述 rtc 驱动会注册生成串口设备/dev/rtcn,应用层的使用只需遵循linux 系统中的标准rtc 编程方法即可。
4.1 打开/关闭rtc 设备 使用标准的文件打开函数:
1 int open(const char *pathname, int flags); 2 int close(int fd);
需要引用头文件:
1 #include 2 #include 3 #include 4 #include
4.2 设置和获取rtc 时间 同样使用标准的ioctl 函数:
1 int ioctl(int d, int request, ...);
需要引用头文件:
1 #include 2 #include
5 模块使用范例 此demo 程序是打开一个rtc 设备,然后设置和获取rtc 时间以及设置闹钟功能。
1 #include /*标准输入输出定义*/ 2 #include /*标准函数库定义*/ 3 #include /*unix 标准函数定义*/ 4 #include 5 #include 6 #include /*文件控制定义*/ 7 #include /*rtc支持的cmd*/ 8 #include /*错误号定义*/ 9 #include 10 11 #define rtc_device_name /dev/rtc0 12 13 int set_rtc_timer(int fd) 14 { 15 struct rtc_time rtc_tm = {0}; 16 struct rtc_time rtc_tm_temp = {0}; 17 18 rtc_tm.tm_year = 2020 - 1900; /* 需要设置的年份,需要减1900 */ 19 rtc_tm.tm_mon = 11 - 1; /* 需要设置的月份,需要确保在0-11范围*/ 20 rtc_tm.tm_mday = 21; /* 需要设置的日期*/ 21 rtc_tm.tm_hour = 10; /* 需要设置的时间*/ 22 rtc_tm.tm_min = 12; /* 需要设置的分钟时间*/ 23 rtc_tm.tm_sec = 30; /* 需要设置的秒数*/ 24 25 /* 设置rtc时间*/ 26 if (ioctl(fd, rtc_set_time, &rtc_tm) < 0) { 27 printf(rtc_set_time failedn); 28 return -1; 29 } 30 31 /* 获取rtc时间*/ 32 if (ioctl(fd, rtc_rd_time, &rtc_tm_temp) < 0) { 33 printf(rtc_rd_time failedn); 34 return -1; 35 } 36 printf(rtc_rd_time return %04d-%02d-%02d %02d:%02d:%02dn, 37 rtc_tm_temp.tm_year + 1900, rtc_tm_temp.tm_mon + 1, rtc_tm_temp.tm_mday, 38 rtc_tm_temp.tm_hour, rtc_tm_temp.tm_min, rtc_tm_temp.tm_sec); 39 return 0; 40 } 41 42 int set_rtc_alarm(int fd) 43 { 44 struct rtc_time rtc_tm = {0}; 45 struct rtc_time rtc_tm_temp = {0}; 46 47 rtc_tm.tm_year = 0; /* 闹钟忽略年设置*/ 48 rtc_tm.tm_mon = 0; /* 闹钟忽略月设置*/ 49 rtc_tm.tm_mday = 0; /* 闹钟忽略日期设置*/ 50 rtc_tm.tm_hour = 10; /* 需要设置的时间*/ 51 rtc_tm.tm_min = 12; /* 需要设置的分钟时间*/ 52 rtc_tm.tm_sec = 30; /* 需要设置的秒数*/ 53 54 /* set alarm time */ 55 if (ioctl(fd, rtc_alm_set, &rtc_tm) < 0) { 56 printf(rtc_alm_set failedn); 57 return -1; 58 } 59 60 if (ioctl(fd, rtc_aie_on) < 0) { 61 printf(rtc_aie_on failed!n); 62 return -1; 63 } 64 65 if (ioctl(fd, rtc_alm_read, &rtc_tm_temp) < 0) { 66 printf(rtc_alm_read failedn); 67 return -1; 68 } 69 70 printf(rtc_alm_read return %04d-%02d-%02d %02d:%02d:%02dn, 71 rtc_tm_temp.tm_year + 1900, rtc_tm_temp.tm_mon + 1, rtc_tm_temp.tm_mday, 72 rtc_tm_temp.tm_hour, rtc_tm_temp.tm_min, rtc_tm_temp.tm_sec); 73 return 0; 74 } 75 76 int main(int argc, char *argv[]) 77 { 78 int fd; 79 int ret; 80 81 /* open rtc device */ 82 fd = open(rtc_device_name, o_rdwr); 83 if (fd < 0) { 84 printf(open rtc device %s failedn, rtc_device_name); 85 return -enodev; 86 } 87 88 /* 设置rtc时间*/ 89 ret = set_rtc_timer(fd); 90 if (ret < 0) { 91 printf(set rtc timer errorn); 92 return -einval; 93 } 94 95 /* 设置闹钟*/ 96 ret = set_rtc_alarm(fd); 97 if (ret /sys/class/sunxi_dump/dump; cat /sys/class/sunxi_dump/dump 0x0000000007000000: 0x00004010 0x00000004 0x0000000f 0x7a000000 0x0000000007000010: 0x00000001 0x00000023 0x00000000 0x00000000 0x0000000007000020: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000030: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000040: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000050: 0x00000001 0x00000000 0x00000000 0x00000000 0x0000000007000060: 0x00000004 0x00000000 0x00000000 0x00000000 0x0000000007000070: 0x00010003 0x00000000 0x00000000 0x00000000 0x0000000007000080: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000090: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000a0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000b0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000c0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000d0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000e0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070000f0: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000100: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000110: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000120: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000130: 0x00000000 0x000030ea 0x04001000 0x00006061 0x0000000007000140: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000150: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000160: 0x083f10f7 0x00000043 0x00000000 0x00000000 0x0000000007000170: 0x00000000 0x00000000 0x00000000 0x00000000 0x0000000007000180: 0x00000000 0x00000000 0x00010001 0x00000000 0x0000000007000190: 0x00000004 0x00000000 0x00000000 0x00000000 0x00000000070001a0: 0x000090ff 0x00000000 0x00000000 0x00000000 0x00000000070001b0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070001c0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070001d0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070001e0: 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000070001f0: 0x00000000 0x00000001 0x00000000 0x00000000 0x0000000007000200: 0x10000000
说明:
每款soc 的模块首地址是不一样的,具体根据spec 或data sheet 确认模块首地址。
LG与软银合作发售高端智能手机G8X,或改变日本智能手机市场的格局
中国邮政水陆两栖无人机具体参数曝光 载荷250公斤 可续航15小时
LGD大尺寸OLED面板出货的飙升得益于OLED电视的热销
无线发射芯片MICRF102的原理及应用
逆变器电路DIY(图文详解)
Linux RTC开发指南
无人机如何帮我们整治卫生问题
工业制造行业中工业自动控制装置市场发展迎来爆发期
三星电子在印度新建的诺伊达全球最大智能手机制造工厂
linux三剑客之awk高效命令集锦!
FLUTE通信协议原理构架
CSDN AI 新战略发布会,探讨 AI 发展趋势
骨传导耳机排行榜10强,五款好用的骨传导耳机推荐
综合布线人应当掌握的9大技巧-clan
无人机倾斜航空摄影的技术特点及如何开展相应的设计
瑞萨电子推出业界领先的低功耗 IGBT,可提高空调运行效率
DIY冷藏室温度监测,数据还可以远程发送到手机
旧电动车电瓶修复技术——不同电池并联充放电的原理变化
能耗监测管理系统,助您实现节能减排!
射频识别技术漫谈(13)——Mifare S50与Mifare S70