44.Linux RTC

44.Linux RTC

RTC同样也是一个标准的字符设备驱动

linux内核为其抽象出来一个rtc_device来描述rtc设备

1.申请并初始化rtc_device

2.注册进linux内核中

3.编写fops字符设备操作集

此结构体定义在 include/linux/rtc.h 文件中

c 复制代码
struct rtc_device
{
	struct device dev;
	struct module *owner;

	int id;
	char name[RTC_DEVICE_NAME_SIZE];

	const struct rtc_class_ops *ops;
	struct mutex ops_lock;

	struct cdev char_dev;
	unsigned long flags;

	unsigned long irq_data;
	spinlock_t irq_lock;
	wait_queue_head_t irq_queue;
	struct fasync_struct *async_queue;

	struct rtc_task *irq_task;
	spinlock_t irq_task_lock;
	int irq_freq;
	int max_user_freq;

	struct timerqueue_head timerqueue;
	struct rtc_timer aie_timer;
	struct rtc_timer uie_rtctimer;
	struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
	int pie_enabled;
	struct work_struct irqwork;
	/* Some hardware can't support UIE mode */
	int uie_unsupported;

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
	struct work_struct uie_task;
	struct timer_list uie_timer;
	/* Those fields are protected by rtc->irq_lock */
	unsigned int oldsecs;
	unsigned int uie_irq_active:1;
	unsigned int stop_uie_polling:1;
	unsigned int uie_task_active:1;
	unsigned int uie_timer_active:1;
#endif
};

const struct rtc_class_ops *ops;为rtc的字符设备操作集,需要自己实现。

c 复制代码
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_mmss64)(struct device *, time64_t secs);
	int (*set_mmss)(struct device *, unsigned long secs);
	int (*read_callback)(struct device *, int data);
	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};

通过设备树的rtc相关节点的compatible很容易找到对应的驱动文件。

c 复制代码
snvs_rtc: snvs-rtc-lp {
					compatible = "fsl,sec-v4.0-mon-rtc-lp";
					regmap = <&snvs>;
					offset = <0x34>;
					interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
				};

rtc-lp:可以使用纽扣电池和系统主电源进行供电、掉电不丢失

rtc-hp:只使用系统电源供电,掉电后数据丢失。

驱动文件:drivers/rtc/rtc-snvs.c

c 复制代码
static const struct of_device_id snvs_dt_ids[] = {
	{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, snvs_dt_ids);

static struct platform_driver snvs_rtc_driver = {
	.driver = {
		.name	= "snvs_rtc",
		.pm	= SNVS_RTC_PM_OPS,
		.of_match_table = snvs_dt_ids,
	},
	.probe		= snvs_rtc_probe,
};
module_platform_driver(snvs_rtc_driver);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
MODULE_LICENSE("GPL");

是一个标准的platform框架

NXP对rtc_device进一步封装成了snvs_rtc_data

c 复制代码
struct snvs_rtc_data *data;

struct snvs_rtc_data {
	struct rtc_device *rtc;
	struct regmap *regmap;
	int offset;
	int irq;
	struct clk *clk;
};

匹配后snvs_rtc_probe函数会执行

主要工作是:

①、寻找设备树定义的rtc寄存器地址

②、将设备树相关信息来初始化snvs_rtc_data的成员变量

​ 主要的重点就是snvs_rtc_data.rtc_device.rtc_class_ops中的函数

c 复制代码
static const struct rtc_class_ops snvs_rtc_ops = {
	.read_time = snvs_rtc_read_time,
	.set_time = snvs_rtc_set_time,
	.read_alarm = snvs_rtc_read_alarm,
	.set_alarm = snvs_rtc_set_alarm,
	.alarm_irq_enable = snvs_rtc_alarm_irq_enable,
};

③、中断设置

④、操作寄存器 基地址加上offset就是rtc_lp的寄存器地址。详细看6ul的参考手册

⑤、申请中断 -》 闹钟

⑥、注册设备 rtc_device_register (设备、名字、操作集、THIS_MODULE)

c 复制代码
struct rtc_device *rtc_device_register(const char *name, 
                                       struct device *dev,
									const struct rtc_class_ops *ops,
									struct module *owner)

name:设备名字。

dev: 设备。

ops: RTC 底层驱动函数集。

owner:驱动模块拥有者。

返回值: 注册成功的话就返回 rtc_device,错误的话会返回一个负值。

当卸载 RTC 驱动的时候需要调用 rtc_device_unregister 函数来注销注册的 rtc_device,函数原型如下:

c 复制代码
void rtc_device_unregister(struct rtc_device *rtc)

rtc:要删除的 rtc_device。返回值: 无。

当应用通过ioctl读取RTC的时间的时候,RTC的核心层rtc_dev_ioctl会执行,通过cmd来决定具体操作,具体操作实例如下:

系统调用字符设备操作集

file_operations.rtc_dev_ioctl

​ ->rtc_read_time

​ 调用驱动设备操作集合rtc_class_ops 中的函数

​ ->rtc_device *rtc -> ops -> read_time

​ -> snvs_rtc_read_time

​ ->rtc_read_lp_counter 获取rtc秒数

​ ->rtc_time_to_tm 将获取到的rtc描述转化成时间

rtc_read_lp_counter 具体实现

c 复制代码
static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
{
	u64 read1, read2;
	u32 val;

	do {
		regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
		read1 = val;
		read1 <<= 32;
		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
		read1 |= val;

		regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
		read2 = val;
		read2 <<= 32;
		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
		read2 |= val;
	/*
	 * when CPU/BUS are running at low speed, there is chance that
	 * we never get same value during two consecutive read, so here
	 * we only compare the second value.
	 */
	} while ((read1 >> CNTR_TO_SECS_SH) != (read2 >> CNTR_TO_SECS_SH));

	/* Convert 47-bit counter to 32-bit raw second count */
	return (u32) (read1 >> CNTR_TO_SECS_SH);
}

read2同理,这里读取了两次 RTC 计数值,因为要读取两个寄存器,因此可能存在读取第二个寄存器的时候时间数据更新了,导致时间不匹配,因此这里连续读两次,如果两次的时间值相等那么就表示时间数据有效。

c 复制代码
while ((read1 >> CNTR_TO_SECS_SH) != (read2 >> CNTR_TO_SECS_SH));

这里只比较前17位,即相同的秒速就认为时间有效

最后返回秒数值

c 复制代码
snvs_rtc 20cc000.snvs:snvs-rtc-lp: rtc core: registered 20cc000.snvs:snvs-r as rtc0
shell 复制代码
//查看RTC时间
 date
//设置当前时间
 date -s "2025-9-3 10:29:00"

大家注意我们使用" date -s"命令仅仅是将当前系统时间设置了,此时间还没有写入到I.MX6U 内部 RTC 里面或其他的 RTC 芯片里面,因此系统重启以后时间又会丢失。我们需要将当前的时间写入到 RTC 里面,这里要用到 hwclock 命令,输入如下命令将系统时间写入到 RTC里面

c 复制代码
hwclock -w //将当前系统时间写入到 RTC 里面

时间写入到 RTC 里面以后就不怕系统重启以后时间丢失了,如果 I.MX6U-ALPHA 开发板底板接了纽扣电池,那么开发板即使断电了时间也不会丢失。

相关推荐
REDcker1 小时前
软件开发者需要关注CPU指令集差异吗?
linux·c++·操作系统·c·cpu·指令集·加密算法
有谁看见我的剑了?1 小时前
Rocky9.6 Samba搭建学习
linux·运维
未来之窗软件服务1 小时前
服务器运维(十六)vlang语言linuxSSH日志分析——东方仙盟炼气期
运维·服务器·服务器运维·东方仙盟
吕了了1 小时前
113 隐藏此电脑中的常用文件夹
运维·windows·系统
Chasing Aurora1 小时前
Python连接云端Linux服务器进行远程 (后端开发/深度学习)时候的注意事项
linux·开发语言·python·ubuntu·ai编程
w***37511 小时前
在 Ubuntu 22.04 上安装和配置 Nginx 的完整指南
linux·nginx·ubuntu
L***B5681 小时前
Nginx代理到https地址忽略证书验证配置
运维·nginx·https
源梦想2 小时前
绝地幸存者H5割草网页小游戏Linux部署演示
linux·运维·服务器
天下·第二2 小时前
python处理【orc】下载压缩的.zip文件,windows和linux同时适配
linux·windows·python