您现在的位置是:首页 >技术交流 >Linux RTC 驱动实验网站首页技术交流

Linux RTC 驱动实验

int data 2024-06-17 11:25:15
简介Linux RTC 驱动实验

RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的, 就和我们使用 Windows 电脑或手机查看时间一样,我们在使用 Linux 设备的时候也需要查看时 间。

一、Linux 内核 RTC 驱动简介

  • RTC 设备驱动是标准的字符设备驱动,应用程序通过 open、release、read、write 和 ioctl 等函数完成对 RTC 设备的操作。
  • Linux 内核将 RTC 设备抽象为 rtc_device 结构体,RTC 设备驱动申请并初始化 rtc_device,将 rtc_device 注册到内核。
  • RTC 设备的操作肯定是用一个操作集合(结构体)来表示的,rtc_device 结构体,此结构体定义在 include/linux/rtc.h 文件中。
  • drivers/rtc/rtc-dev.c 是Linux 内核提供的 RTC 通用字符设备驱动文件,结构体定义在 include/linux/rtc.h 文件中。
struct rtc_device
 {
 struct device dev; /* 设备 */
 struct module *owner;

 int id; /* ID */ 
 char name[RTC_DEVICE_NAME_SIZE]; /* 名字 */
 
 const struct rtc_class_ops *ops; /* RTC 设备底层操作函数 */
 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;
};

 我们需要重点关注的是 ops 成员变量,这是一个 rtc_class_ops 类型的指针变量,rtc_class_ops 为 RTC 设备的最底层操作函数集合,包括从 RTC 设备中读取时间、向 RTC 设备写入新的时间 值等。因此,rtc_class_ops 是需要用户根据所使用的 RTC 设备编写的,此结构体定义在 include/linux/rtc.h 文件中,内容如下:

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_class_ops 中的这些函数只是最底层的 RTC 设备操作函数,并不是提供给应用层的 file_operations 函数操作集。RTC 是个字符设备,那么肯定有字符设备的 file_operations 函数操 作集,Linux 内核提供了一个 RTC 通用字符设备驱动文件,文件名为 drivers/rtc/rtc-dev.c,rtcdev.c 文件提供了所有 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_class_ops 准备好以后需要将其注册到 Linux 内核中,这里我们可以使用 rtc_device_register函数完成注册工作。此函数会申请一个rtc_device并且初始化这个rtc_device, 最后向调用者返回这个 rtc_device,此函数原型如下:

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,函数 原型如下:

void rtc_device_unregister(struct rtc_device *rtc)
/*
函数参数和返回值含义如下:
rtc:要删除的 rtc_device。
返回值:无。
*/

还有另外一对 rtc_device 注册函数 devm_rtc_device_registerdevm_rtc_device_unregister, 分别为注册和注销 rtc_device。

二、I.MX6U 内部 RTC 驱动分析

  • RTC 底层驱动集为 snvs_rtc_ops。snvs_rtc_ops操作集包含了读取/设置RTC时间,读取/设置闹钟等函数。
  • imx6ull.dtsi,设备节点名为 snvs_rtc。compatible 的值为“fsl,sec-v4.0-mon-rtc-lp”,驱动文件为 drivers/rtc/rtc-snvs.c。
  • 设备树 ID 表,compatible 属性,值为“fsl,sec-v4.0-mon-rtc-lp”, imx6ull.dtsi 中的 snvs_rtc 设备节点会和此驱动匹配。 
  • 当设备和驱动匹配成功以后 snvs_rtc_probe 函数就会执行。
  • platform_get_resource 函数从设备树中获取到 RTC 外设寄存器基地址。
  • devm_ioremap_resource 完成内存映射,得到 RTC 外设寄存器物理基地址对应的虚拟地址。
  • devm_regmap_init_mmio 函数将 RTC 的硬件寄存器转化为 regmap 形式。
  • snvs_rtc_enable 函数使能 RTC,此函数会设置 RTC_LPCR 寄存器。
  • devm_request_irq函数请求RTC中断。
  • snvs_rtc_irq_handler为中断服务函数,用于 RTC 闹钟中断。
  • snvs_rtc_read_time 函数用于读取 RTC 时间值。
  • rtc_read_lp_counter 获取 RTC 计数值,这个时间值是秒数。
  • rtc_time_to_tm 函数将获取到的秒数转换为时间值。
  • rtc_read_lp_counter 函数,此函数用于读取 RTC 计数值。

三、RTC 时间查看与设置

1、时间 RTC 查看

  • Linux 内核在启动的时候将 snvs_rtc 设置为 rtc0。
  • Linux 内核启动的时候可以看到系统时钟设置信息:

 使用“date”命令查看时间:

 2、设置 RTC 时间

date -s "2023-05-20 9:30:00"        //设置当前系统时间
hwclock -w                            //将当前系统时间写入到 RTC 里面

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。