RTC驱动模型 一、概述 1.在Linux中有硬件时钟与系统时钟等两种时钟。硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟。系统时钟则是指kernel中的时钟。当Linux启动时,系统时钟会去读取硬件时钟的设定,之后系统时钟即独立运作。所有Linux相关指令与函数都是读取系统时钟的设定。 系统时钟的设定就是我们常用的date命令,而我们写的RTC驱动就是为硬件时钟服务的,它有属于自己的命令hwclock,因此使用date命令是不可能调用到我们的驱动的,我们可以通过hwclock的一些指令来实现更新rtc时钟——也就是系统时钟和硬件时钟的交互。 hwclock –r 显示硬件时钟与日期 hwclock –s 将系统时钟调整为与目前的硬件时钟一致。 hwclock –w 将硬件时钟调整为与目前的系统时钟一致。 2.RTC核心文件: /include/linux/rtc.h 定义了与RTC有关的数据结构 /drivers/rtc/hctosys.c 在启动时初始化系统时间。 /drivers/rtc/rtc-lib.c 提供了一些时间格式相互转化的函数。 /drivers/rtc/class.c 这个文件向linux设备模型核心注册了一个类RTC,然后向驱动程序提供了注册/注销接口 /drivers/rtc/interface.c 这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数 /drivers/rtc/rtc-dev.c: 字符设备的注册和用户层文件操作函数接口。 /drivers/rtc/rtc-proc.c 与proc文件系统有关 /drivers/rtc/rtc-sysfs.c 与sysfs有关 /drivers/rtc/rtc-omap.c: davinci RTC的芯片平台驱动。 3.驱动模型结构 二. 基本数据结构 1. struct rtc_device 结构 //这个结构是RTC驱动程序的基本数据结构,但是他不像其他核心的基本结构一样,驱动程序以他为参数调用注册函数注册到核心。这个结构是由注册函数返回给驱动程序的。 struct rtc_device { struct device dev; struct module *owner; int id; char name[RTC_DEVICE_NAME_SIZE];//RTC名称 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; #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 }; 2. struct rtc_class_ops 结构 //这个结构是RTC驱动程序要实现的基本操作函数,注意这里的操作不是文件操作。驱动程序通过初始化这样一个结构,将自己实现的函数与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 (*irq_set_state)(struct device *, int enabled); int (*irq_set_freq)(struct device *, int freq); int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); int (*update_irq_enable)(struct device *, unsigned int enabled); }; 3. struct rtc_time 结构 //代表了时间与日期,从RTC设备读回的时间和日期就保存在这个结构体中 struct rtc_time { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; 三、RTC驱动过程 1.platform 设备注册 //在board-da850-evm.c中注册RTC的platform设备。 static __init void da850_evm_init(void) { 。。。。。。 ret = da8xx_register_rtc(); if (ret) pr_warning("da850_evm_init: rtc setup failed: %dn", ret); 。。。。。。 } static struct resource da8xx_rtc_resources[] = { { .start = DA8XX_RTC_BASE,//#define DA8XX_RTC_BASE 0x01C23000 .end = DA8XX_RTC_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { /* timer irq */ .start = IRQ_DA8XX_RTC,//中断号是19 .end = IRQ_DA8XX_RTC, .flags = IORESOURCE_IRQ, }, { /* alarm irq */ .start = IRQ_DA8XX_RTC, .end = IRQ_DA8XX_RTC, .flags = IORESOURCE_IRQ, }, }; static struct platform_device da8xx_rtc_device = { .name = "omap_rtc", .id = -1, .num_resources = ARRAY_SIZE(da8xx_rtc_resources), .resource = da8xx_rtc_resources, }; int da8xx_register_rtc(void) { int ret; void __iomem *base; base = ioremap(DA8XX_RTC_BASE, SZ_4K);//RTC寄存器基地址映射到内存中 if (WARN_ON(!base)) return -ENOMEM; //写RTC寄存器,解除RTC的写保护 __raw_writel(0x83e70b13, base + 0x6c); __raw_writel(0x95a4f1e0, base + 0x70); iounmap(base);//使用虚拟地址访问物理空间,访问结束后,释放本次映射 ret = platform_device_register(&da8xx_rtc_device);//注册platform设备 if (!ret)/* Atleast on DA850, RTC is a wakeup source */ device_init_wakeup(&da8xx_rtc_device.dev, true); return ret; } 2.调用class_create创建了一个类--rtc。我们知道类是一个设备的高层视图,他抽象出了底层的实现细节。 //driversrtcclass.c static int __init rtc_init(void) { rtc_class = class_create(THIS_MODULE, "rtc");//为sysfs文件系统创建一个类rtc_class if (IS_ERR(rtc_class)) { printk(KERN_ERR "%s: couldn't create classn", __FILE__); return PTR_ERR(rtc_class); } rtc_class->suspend = rtc_suspend; rtc_class->resume = rtc_resume; rtc_dev_init();//为RTC设备动态分配一个设备号 rtc_sysfs_init(rtc_class);//设置属性 return 0; } //driversrtcrtc-dev.c void __init rtc_dev_init(void) { int err; err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");//动态分配设备号,赋值给rtc_devt if (err < 0) printk(KERN_ERR "%s: failed to allocate char dev regionn",__FILE__); } //driversrtcrtc-sysfs.c static struct device_attribute rtc_attrs[] = { __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,rtc_sysfs_set_max_user_freq), __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), { }, }; void __init rtc_sysfs_init(struct class *rtc_class) { //sysfs文件系统会根据这些属性创建属性文件 rtc_class->dev_attrs = rtc_attrs;//RTC属性 } 3.注册RTC设备的platform driver //在目录driversrtcrtc-omap.c static struct rtc_class_ops omap_rtc_ops = { .ioctl = omap_rtc_ioctl, .read_time = omap_rtc_read_time, .set_time = omap_rtc_set_time, .read_alarm = omap_rtc_read_alarm, .set_alarm = omap_rtc_set_alarm, }; static int omap_rtc_alarm;//RTC的alarm中断号 static int omap_rtc_timer;//RTC的timer中断号 static struct platform_driver omap_rtc_driver = { .remove = __exit_p(omap_rtc_remove), .suspend = omap_rtc_suspend, .resume = omap_rtc_resume, .shutdown = omap_rtc_shutdown, .driver = { .name = "omap_rtc", .owner = THIS_MODULE, }, }; static int __init rtc_init(void) { //注册RTC的platform driver驱动,platform的设备和驱动配对成功后,会调用omap_rtc_probe()函数 return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe); } module_init(rtc_init); static int __init omap_rtc_probe(struct platform_device *pdev) { struct resource *res, *mem; struct rtc_device *rtc; u8 reg, new_ctrl; omap_rtc_timer = platform_get_irq(pdev, 0);//从platform_device资源中获得RTC的timer中断号 if (omap_rtc_timer <= 0) { pr_debug("%s: no update irq?n", pdev->name); return -ENOENT; } omap_rtc_alarm = platform_get_irq(pdev, 1);//从platform_device资源中获得RTC的alarm中断号 if (omap_rtc_alarm <= 0) { pr_debug("%s: no alarm irq?n", pdev->name); return -ENOENT; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//从platform_device资源中获得RTC的寄存器地址资源 if (!res) { pr_debug("%s: RTC resource data missingn", pdev->name); return -ENOENT; } mem = request_mem_region(res->start, resource_size(res), pdev->name);//申请内存资源 if (!mem) { pr_debug("%s: RTC registers at %08x are not freen", pdev->name, res->start); return -EBUSY; } rtc_base = ioremap(res->start, resource_size(res));//将RTC寄存器物理地址映射到内存虚拟地址 if (!rtc_base) { pr_debug("%s: RTC registers can't be mappedn", pdev->name); goto fail; } rtc = rtc_device_register(pdev->name, &pdev->dev,&omap_rtc_ops, THIS_MODULE);//注册RTC设备 if (IS_ERR(rtc)) { pr_debug("%s: can't register RTC device, err %ldn",pdev->name, PTR_ERR(rtc)); goto fail0; } platform_set_drvdata(pdev, rtc);//将RTC数据结构rtc_device保存在platform_device结构中的device字段中 dev_set_drvdata(&rtc->dev, mem);//将资源保存 //Disable RTC的alarm和timer中断,并且设置秒周期 rtc_write(0, OMAP_RTC_INTERRUPTS_REG); /* clear old status */ reg = rtc_read(OMAP_RTC_STATUS_REG);//读RTC状态寄存器 if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {//davinci平台没有第7位的状态 pr_info("%s: RTC power up reset detectedn",pdev->name); rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG); } if (reg & (u8) OMAP_RTC_STATUS_ALARM)//如果有alarm中断产生 rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);//此位写1清除alarm中断状态 //申请中断并设置中断函数 if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,dev_name(&rtc->dev), rtc)) { pr_debug("%s: RTC timer interrupt IRQ%d already claimedn",pdev->name, omap_rtc_timer); goto fail1; } if ((omap_rtc_timer != omap_rtc_alarm) &&(request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,dev_name(&rtc->dev), rtc))) { pr_debug("%s: RTC alarm interrupt IRQ%d already claimedn",pdev->name, omap_rtc_alarm); goto fail2; } reg = rtc_read(OMAP_RTC_CTRL_REG);//读RTC控制寄存器 if (reg & (u8) OMAP_RTC_CTRL_STOP)//查看RTC是否在运行 pr_info("%s: already runningn", pdev->name); //采用24H模式,不自主补偿矫正时间,并且Disable split power. new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP); new_ctrl |= OMAP_RTC_CTRL_STOP;//启动RTC计数 if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT) pr_info("%s: split power moden", pdev->name); if (reg != new_ctrl) rtc_write(new_ctrl, OMAP_RTC_CTRL_REG); return 0; fail2: free_irq(omap_rtc_timer, NULL); fail1: rtc_device_unregister(rtc); fail0: iounmap(rtc_base); fail: release_resource(mem); return -EIO; } 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; int id, err; //处理一个idr的结构,idr在linux内核中指的就是整数ID管理机制,从本质上来说,idr是一种将整数ID号和特定指针关联在一起的机制。这里从内核中获取一个idr结构,并与id相关联。 if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { err = -ENOMEM; goto exit; } mutex_lock(&idr_lock); err = idr_get_new(&rtc_idr, NULL, &id); mutex_unlock(&idr_lock); if (err < 0) goto exit; id = id & MAX_ID_MASK; //分配RTC设备的结构体 rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); if (rtc == NULL) { err = -ENOMEM; goto exit_idr; } //对结构体进行初始化 rtc->id = id; rtc->ops = ops;//rtc的底层驱动操作函数 rtc->owner = owner; rtc->max_user_freq = 64; rtc->dev.parent = dev;//rtc_device设备的父设备是platform设备中的device结构 rtc->dev.class = rtc_class;//指向rtc的类 rtc->dev.release = rtc_device_release; mutex_init(&rtc->ops_lock); spin_lock_init(&rtc->irq_lock); spin_lock_init(&rtc->irq_task_lock); init_waitqueue_head(&rtc->irq_queue); strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);//设置名称为omap_rtc dev_set_name(&rtc->dev, "rtc%d", id);//设置RTC设备名称,生成设备节点就是这个名称 //因为RTC设备本质来讲还是字符设备,所以这里初始化了字符设备相关的结构:设备号以及文件操作。 rtc_dev_prepare(rtc);//RTC字符设备注册的准备 err = device_register(&rtc->dev);//RTC设备注册,生成sysfs文件系统相关的诸多文件和目录,udev会根据生成的dev文件创建设备节点 if (err) { put_device(&rtc->dev); goto exit_kfree; } rtc_dev_add_device(rtc);//添加RTC的字符设备 rtc_sysfs_add_device(rtc);//为设备添加了一个闹钟属性 rtc_proc_add_device(rtc);//创建proc文件系统接口 dev_info(dev, "rtc core: registered %s as %sn",rtc->name, dev_name(&rtc->dev)); return rtc; exit_kfree: kfree(rtc); exit_idr: mutex_lock(&idr_lock); idr_remove(&rtc_idr, id); mutex_unlock(&idr_lock); exit: dev_err(dev, "rtc core: unable to register %s, err = %dn",name, err); return ERR_PTR(err); } //用户层文件操作函数接口 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, }; void rtc_dev_prepare(struct rtc_device *rtc) { if (!rtc_devt) return; if (rtc->id >= RTC_DEV_MAX) { pr_debug("%s: too many RTC devicesn", rtc->name); return; } rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);//获得设备号 #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL INIT_WORK(&rtc->uie_task, rtc_uie_task); setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc); #endif cdev_init(&rtc->char_dev, &rtc_dev_fops); rtc->char_dev.owner = rtc->owner; } void rtc_dev_add_device(struct rtc_device *rtc) { //添加字符设备 if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) printk(KERN_WARNING "%s: failed to add char device %d:%dn",rtc->name, MAJOR(rtc_devt), rtc->id); else pr_debug("%s: dev (%d:%d)n", rtc->name,MAJOR(rtc_devt), rtc->id); } void rtc_sysfs_add_device(struct rtc_device *rtc) { int err; /* not all RTCs support both alarms and wakeup */ if (!rtc_does_wakealarm(rtc)) return; err = device_create_file(&rtc->dev, &dev_attr_wakealarm); if (err) dev_err(rtc->dev.parent,"failed to create alarm attribute, %dn", err); } 四、用户层应用 1.读时间 用户打开设备后,通过ioctl命令发出读时间操作,进而调用interface.c中的rtc_read_time() int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) { int err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; if (!rtc->ops) err = -ENODEV; else if (!rtc->ops->read_time) err = -EINVAL; else { memset(tm, 0, sizeof(struct rtc_time)); err = rtc->ops->read_time(rtc->dev.parent, tm);//调用底层驱动函数omap_rtc_read_time() } mutex_unlock(&rtc->ops_lock); return err; } static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm) { /* we don't report wday/yday/isdst ... */ local_irq_disable(); rtc_wait_not_busy(); //读RTC寄存器 tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG); tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG); tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG); tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG); tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG); tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG); local_irq_enable(); bcd2tm(tm); return 0; } 2.写时间 int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) { int err; err = rtc_valid_tm(tm); if (err != 0) return err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; 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_mmss) { unsigned long secs; err = rtc_tm_to_time(tm, &secs); if (err == 0) err = rtc->ops->set_mmss(rtc->dev.parent, secs);//调用omap_rtc_set_time } else err = -EINVAL; mutex_unlock(&rtc->ops_lock); return err; } static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) { if (tm2bcd(tm) < 0) return -EINVAL; local_irq_disable(); rtc_wait_not_busy(); rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG); rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG); rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG); rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG); rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG); rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG); local_irq_enable(); return 0; }