平台:RK3568 内核版本:4.19.194
RTC简介
RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的,使用 Linux 设备的时候也需要查看时间。RTC是Linux的时间系统。
RTC 设备驱动是一个标准的字符设备驱动,应用程序通过 open、 release、 read、 write 和 ioctl 等函数完成对 RTC 设备的操作。
RTC相关结构体
struct rtc_device {
struct device dev;
struct module *owner;
int id;
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;
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;
int pie_enabled;
struct work_struct irqwork;
int uie_unsupported;
long set_offset_nsec;
bool registered;
struct nvmem_device *nvmem;
bool nvram_old_abi;
struct bin_attribute *nvram;
time64_t range_min;
timeu64_t range_max;
time64_t start_secs;
time64_t offset_secs;
bool set_start_time;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
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
};
struct rtc_class_ops {
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);
int (*read_offset)(struct device *, long *offset);
int (*set_offset)(struct device *, long offset);
};
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整体调用框架
RTC代码解析
涉及到的目录功能总结
- class.c:向linux设备模型核心注册了一个类RTC,提供了RTC子系统的一些公共函数,让各个RTC驱动注册集成到我们的linux内核中,向驱动程序提供了注册/注销接口。
- rtc-dev.c:定义了基本的设备文件操作函数,用户程序与RTC驱动的接口函数,这里定义了每个ioctl命令需要调用的函数,还有open,read等。
- interface.c:提供了ioctl各个命令需要调用的函数。
- rtc-sysfs.c:与sysfs有关,提供通过sys文件系统操作pcf8563。
- rtc-proc.c:与proc文件系统有关,提供通过proc文件系统操作pcf8563。
- hctosys.c:系统起来之后会调用到这个文件中的rtc_hctosys()函数,主要功能是系统起来的时候去读RTC硬件中的时间,然后更新我们的系统时间。
- rtc.h:定义了与RTC有关的数据结构。
RTC驱动注册函数解析
struct class *rtc_class;
rtc_init
-> rtc_class = class_create(THIS_MODULE, "rtc");
-> rtc_class->pm = RTC_CLASS_DEV_PM_OPS;
-> rtc_dev_init();
-> alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
subsys_initcall(rtc_init);
rtc_device_register
-> id = rtc_device_get_id(dev);
-> ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
-> rtc = rtc_allocate_device();
-> struct rtc_device *rtc;
-> rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
-> ...... rtc->dev.class = rtc_class; ......
-> mutex_init(&rtc->ops_lock);
-> spin_lock_init(&rtc->irq_lock);
-> init_waitqueue_head(&rtc->irq_queue);
-> timerqueue_init_head(&rtc->timerqueue);
-> INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
-> rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
-> rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
-> hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-> rtc->pie_timer.function = rtc_pie_update_irq;
-> rtc->pie_enabled = 0;
-> rtc->id = id;
-> rtc->ops = ops;
-> rtc->owner = owner;
-> rtc->dev.parent = dev;
-> dev_set_name(&rtc->dev, "rtc%d", id);
-> __rtc_read_alarm(rtc, &alrm);
-> if (!err && !rtc_valid_tm(&alrm.time))
rtc_initialize_alarm(rtc, &alrm);
-> rtc_dev_prepare(rtc);
-> cdev_init(&rtc->char_dev, &rtc_dev_fops);
-> cdev->ops = fops;
-> cdev_device_add(&rtc->char_dev, &rtc->dev);
-> cdev_add(cdev, dev->devt, 1);
-> rtc_proc_add_device(rtc);
-> proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, rtc);
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,
};
kernel中__init类型函数都位于.init.text段中,对应的在.initcall.init段中保存相应的函数指针。系统在启动过程中,根据定义在段中的等级值(0~7)从低到高依次执行。定义:
#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)
应用层调用驱动流程解析
rtc_dev_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
-> struct rtc_device *rtc = file->private_data;
-> switch (cmd)
-> case RTC_RD_TIME:
-> rtc_read_time
-> __rtc_read_time
-> rtc->ops->read_time(rtc->dev.parent, tm);
-> case RTC_SET_TIME:
-> rtc_set_time
-> rtc_valid_tm(tm);
-> if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm);
else if (rtc->ops->set_mmss64) {
time64_t secs64 = rtc_tm_to_time64(tm);
err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
|