Linux rtc驱动模块分析

内核版本:linux-2.6.32
rtc驱动模块在drivers/rtc目录下,首先来看模块的初始化和卸载函数,在class.c中:

211 static int __init rtc_init(void) 212 { 213rtc_class = class_create(THIS_MODULE, "rtc"); 214if (IS_ERR(rtc_class)) { 215printk(KERN_ERR "%s: couldn't create class\n", __FILE__); 216return PTR_ERR(rtc_class); 217} 218rtc_class->suspend = rtc_suspend; 219rtc_class->resume = rtc_resume; 220rtc_dev_init(); 221rtc_sysfs_init(rtc_class); 222return 0; 223 } 224 225 static void __exit rtc_exit(void) 226 { 227rtc_dev_exit(); 228class_destroy(rtc_class); 229 } 230 231 subsys_initcall(rtc_init); 232 module_exit(rtc_exit);

在模块的初始化函数中,完成了三件事情:
1. 调用class_create创建了一个class
2. 调用rtc_dev_init()
3. 调用rtc_sysfs_init()
而卸载函数则完成相反的动作。

在class.c中如果除去同PM相关的suspend和resume操作外,主要有两个函数:rtc_device_register和rtc_device_unregister。从这两个函数的名字来看,应该是rtc设备的注册和注销函数,先来看rtc_device_register函数:
104 /** 105* rtc_device_register - register w/ RTC class 106* @dev: the device to register 107* 108* rtc_device_unregister() must be called when the class device is no 109* longer needed. 110* 111* Returns the pointer to the new struct class device. 112*/ 113 struct rtc_device *rtc_device_register(const char *name, struct device *dev, 114const struct rtc_class_ops *ops, 115struct module *owner) 116 { 117struct rtc_device *rtc; 118int id, err; 119 120if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { 121err = -ENOMEM; 122goto exit; 123} 124 125 126mutex_lock(&idr_lock); 127err = idr_get_new(&rtc_idr, NULL, &id); 128mutex_unlock(&idr_lock); 129 130if (err < 0) 131goto exit; 132 133id = id & MAX_ID_MASK; 134 135rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); 136if (rtc == NULL) { 137err = -ENOMEM; 138goto exit_idr; 139} 140 141rtc->id = id; 142rtc->ops = ops; 143rtc->owner = owner; 144rtc->max_user_freq = 64; 145rtc->dev.parent = dev; 146rtc->dev.class = rtc_class; 147rtc->dev.release = rtc_device_release; 148 149mutex_init(&rtc->ops_lock); 150spin_lock_init(&rtc->irq_lock); 151spin_lock_init(&rtc->irq_task_lock); 152init_waitqueue_head(&rtc->irq_queue); 153 154strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); 155dev_set_name(&rtc->dev, "rtc%d", id); 156 157rtc_dev_prepare(rtc); 158 159err = device_register(&rtc->dev); 160if (err) 161goto exit_kfree; 162 163rtc_dev_add_device(rtc); 164rtc_sysfs_add_device(rtc); 165rtc_proc_add_device(rtc); 166 167dev_info(dev, "rtc core: registered %s as %s\n", 168rtc->name, dev_name(&rtc->dev)); 169 170return rtc; 171 172 exit_kfree: 173kfree(rtc); 174 175 exit_idr: 176mutex_lock(&idr_lock); 177idr_remove(&rtc_idr, id); 178mutex_unlock(&idr_lock); 179 180 exit: 181dev_err(dev, "rtc core: unable to register %s, err = %d\n", 182name, err); 183return ERR_PTR(err); 184 } 185 EXPORT_SYMBOL_GPL(rtc_device_register);

rtc设备使用struct rtc_device结构来描述,那么除去idr部分,从135行开始看起。
首先是为rtc设备申请内存,然后是对rtc设备的一些赋值操作,比如id、ops等等。152行,初始化了一个等待队列,157行,调用rtc_dev_prepare,应该是rtc-dev.c中的函数,可以先去看看:
484 void rtc_dev_prepare(struct rtc_device *rtc) 485 { 486if (!rtc_devt) 487return; 488 489if (rtc->id >= RTC_DEV_MAX) { 490pr_debug("%s: too many RTC devices\n", rtc->name); 491return; 492} 493 494rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); 495 496 #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 497INIT_WORK(&rtc->uie_task, rtc_uie_task); 498setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc); 499 #endif 500 501cdev_init(&rtc->char_dev, &rtc_dev_fops); 502rtc->char_dev.owner = rtc->owner; 503 }

我们看到,最后调用了cdev_init初始化了一个字符设备。
再回到rtc_device_register函数中,159行,调用device_register注册这个rtc设备,163行,调用rtc_dev_add_device,前面是初始化了一个字符设备,那么这个函数应该是注册这个字符设备,代码如下:
505 void rtc_dev_add_device(struct rtc_device *rtc) 506 { 507if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) 508printk(KERN_WARNING "%s: failed to add char device %d:%d\n", 509rtc->name, MAJOR(rtc_devt), rtc->id); 510else 511pr_debug("%s: dev (%d:%d)\n", rtc->name, 512MAJOR(rtc_devt), rtc->id); 513 }


剩下的两个函数是rtc_sysfs_add_device和rtc_proc_add_device,自然是向sysfs和proc文件系统添加这两个设备。

rtc_device_unregister函数如下:
188 /** 189* rtc_device_unregister - removes the previously registered RTC class device 190* 191* @rtc: the RTC class device to destroy 192*/ 193 void rtc_device_unregister(struct rtc_device *rtc) 194 { 195if (get_device(&rtc->dev) != NULL) { 196mutex_lock(&rtc->ops_lock); 197/* remove innards of this RTC, then disable it, before 198* letting any rtc_class_open() users access it again 199*/ 200rtc_sysfs_del_device(rtc); 201rtc_dev_del_device(rtc); 202rtc_proc_del_device(rtc); 203device_unregister(&rtc->dev); 204rtc->ops = NULL; 205mutex_unlock(&rtc->ops_lock); 206put_device(&rtc->dev); 207} 208 } 209 EXPORT_SYMBOL_GPL(rtc_device_unregister);

没有什么好说的。

从这里我们可以了解到,如果要使用linux提供的rtc模块来写rtc驱动的话,首先应该定义一个rtc_device结构,然后调用rtc_device_register去注册这个rtc设备,那么再看具体驱动之前,还是来先看dev、sysfs和proc相关的东西,看看他们到底提供了什么接口。



1. rtc中的char device
在rtc_init函数中首先调用了rtc_dev_init函数,函数如下:
521 void __init rtc_dev_init(void) 522 { 523int err; 524 525err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); 526if (err < 0) 527printk(KERN_ERR "%s: failed to allocate char dev region\n", 528__FILE__); 529 }

可见,该函数只是申请了一个字符设备号。而与之对应的rtc_dev_exit肯定是注销申请的设备号,代码如下:
531 void __exit rtc_dev_exit(void) 532 { 533if (rtc_devt) 534unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); 535 }



而rtc_dev_prepare和rtc_dev_add_device这两个函数已经看了,分别完成字符设备的初始化和注册功能。那么字符设备肯定会提供一些接口,比如open、read和write等等,所以rtc的file_operations定义如下:
471 static const struct file_operations rtc_dev_fops = { 472.owner= THIS_MODULE, 473.llseek= no_llseek, 474.read= rtc_dev_read, 475.poll= rtc_dev_poll, 476.unlocked_ioctl = rtc_dev_ioctl, 477.open= rtc_dev_open, 478.release= rtc_dev_release, 479.fasync= rtc_dev_fasync, 480 };


先来看open操作:
23 static int rtc_dev_open(struct inode *inode, struct file *file) 24 { 25int err; 26struct rtc_device *rtc = container_of(inode->i_cdev, 27struct rtc_device, char_dev); 28const struct rtc_class_ops *ops = rtc->ops; 29 30if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) 31return -EBUSY; 32 33file->private_data = https://www.it610.com/article/rtc; 34 35err = ops->open ? ops->open(rtc->dev.parent) : 0; 36if (err == 0) { 37spin_lock_irq(&rtc->irq_lock); 38rtc->irq_data = https://www.it610.com/article/0; 39spin_unlock_irq(&rtc->irq_lock); 40 41return 0; 42} 43 44/* something has gone wrong */ 45clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); 46return err; 47 }

在rtc_dev_open函数中,并无实际操作,如果rtc设备提供了open操作,则调用rtc设备的open函数,否则直接返回0。

而release操作中也并未做太多事情,调用了ioctl函数的RTC_UIE_OFF操作和调用rtc设备的release方法,代码如下:
446 static int rtc_dev_release(struct inode *inode, struct file *file) 447 { 448struct rtc_device *rtc = file->private_data; 449 450/* We shut down the repeating IRQs that userspace enabled, 451* since nothing is listening to them. 452*- Update (UIE) ... currently only managed through ioctls 453*- Periodic (PIE) ... also used through rtc_*() interface calls 454* 455* Leave the alarm alone; it may be set to trigger a system wakeup 456* later, or be used by kernel code, and is a one-shot event anyway. 457*/ 458 459/* Keep ioctl until all drivers are converted */ 460rtc_dev_ioctl(file, RTC_UIE_OFF, 0); 461rtc_update_irq_enable(rtc, 0); 462rtc_irq_set_state(rtc, NULL, 0); 463 464if (rtc->ops->release) 465rtc->ops->release(rtc->dev.parent); 466 467clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); 468return 0; 469 }


再来看read操作:
149 static ssize_t 150 rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 151 { 152struct rtc_device *rtc = file->private_data; 153 154DECLARE_WAITQUEUE(wait, current); 155unsigned long data; 156ssize_t ret; 157 158if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) 159return -EINVAL; 160 161add_wait_queue(&rtc->irq_queue, &wait); 162do { 163__set_current_state(TASK_INTERRUPTIBLE); 164 165spin_lock_irq(&rtc->irq_lock); 166data = https://www.it610.com/article/rtc->irq_data; 167rtc->irq_data = https://www.it610.com/article/0; 168spin_unlock_irq(&rtc->irq_lock); 169 170if (data != 0) { 171ret = 0; 172break; 173} 174if (file->f_flags & O_NONBLOCK) { 175ret = -EAGAIN; 176break; 177} 178if (signal_pending(current)) { 179ret = -ERESTARTSYS; 180break; 181} 182schedule(); 183} while (1); 184set_current_state(TASK_RUNNING); 185remove_wait_queue(&rtc->irq_queue, &wait); 186 187if (ret == 0) { 188/* Check for any data updates */ 189if (rtc->ops->read_callback) 190data = https://www.it610.com/article/rtc->ops->read_callback(rtc->dev.parent, 191data); 192 193if (sizeof(int) != sizeof(long) && 194count == sizeof(unsigned int)) 195ret = put_user(data, (unsigned int __user *)buf) ?: 196sizeof(unsigned int); 197else 198ret = put_user(data, (unsigned long __user *)buf) ?: 199sizeof(unsigned long); 200} 201return ret; 202 }

调用read函数时,可能会没有数据供应用程序读取,所以提供了等待队列,在没有数据读取时阻塞read操作。最后调用的是rtc设备的read_callback方法,最后将读取到的数据返回给应用程序。

再来看ioctl操作,代码如下:
216 static long rtc_dev_ioctl(struct file *file, 217unsigned int cmd, unsigned long arg) 218 { 219int err = 0; 220struct rtc_device *rtc = file->private_data; 221const struct rtc_class_ops *ops = rtc->ops; 222struct rtc_time tm; 223struct rtc_wkalrm alarm; 224void __user *uarg = (void __user *) arg; 225 226err = mutex_lock_interruptible(&rtc->ops_lock); 227if (err) 228return err; 229 230/* check that the calling task has appropriate permissions 231* for certain ioctls. doing this check here is useful 232* to avoid duplicate code in each driver. 233*/ 234switch (cmd) { 235case RTC_EPOCH_SET: 236case RTC_SET_TIME: 237if (!capable(CAP_SYS_TIME)) 238err = -EACCES; 239break; 240 241case RTC_IRQP_SET: 242if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) 243err = -EACCES; 244break; 245 246case RTC_PIE_ON: 247if (rtc->irq_freq > rtc->max_user_freq && 248!capable(CAP_SYS_RESOURCE)) 249err = -EACCES; 250break; 251} 252 253if (err) 254goto done; 255 256/* try the driver's ioctl interface */ 257if (ops->ioctl) { 258err = ops->ioctl(rtc->dev.parent, cmd, arg); 259if (err != -ENOIOCTLCMD) { 260mutex_unlock(&rtc->ops_lock); 261return err; 262} 263} 264 265/* if the driver does not provide the ioctl interface 266* or if that particular ioctl was not implemented 267* (-ENOIOCTLCMD), we will try to emulate here. 268* 269* Drivers *SHOULD NOT* provide ioctl implementations 270* for these requests.Instead, provide methods to 271* support the following code, so that the RTC's main 272* features are accessible without using ioctls. 273* 274* RTC and alarm times will be in UTC, by preference, 275* but dual-booting with MS-Windows implies RTCs must 276* use the local wall clock time. 277*/ 278 279switch (cmd) { 280case RTC_ALM_READ: 281mutex_unlock(&rtc->ops_lock); 282 283err = rtc_read_alarm(rtc, &alarm); 284if (err < 0) 285return err; 286 287if (copy_to_user(uarg, &alarm.time, sizeof(tm))) 288err = -EFAULT; 289return err; 290 291case RTC_ALM_SET: 292mutex_unlock(&rtc->ops_lock); 293 294if (copy_from_user(&alarm.time, uarg, sizeof(tm))) 295return -EFAULT; 296 297alarm.enabled = 0; 298alarm.pending = 0; 299alarm.time.tm_wday = -1; 300alarm.time.tm_yday = -1; 301alarm.time.tm_isdst = -1; 302 303/* RTC_ALM_SET alarms may be up to 24 hours in the future. 304* Rather than expecting every RTC to implement "don't care" 305* for day/month/year fields, just force the alarm to have 306* the right values for those fields. 307* 308* RTC_WKALM_SET should be used instead.Not only does it 309* eliminate the need for a separate RTC_AIE_ON call, it 310* doesn't have the "alarm 23:59:59 in the future" race. 311* 312* NOTE:some legacy code may have used invalid fields as 313* wildcards, exposing hardware "periodic alarm" capabilities. 314* Not supported here. 315*/ 316{ 317unsigned long now, then; 318 319err = rtc_read_time(rtc, &tm); 320if (err < 0) 321return err; 322rtc_tm_to_time(&tm, &now); 323 324alarm.time.tm_mday = tm.tm_mday; 325alarm.time.tm_mon = tm.tm_mon; 326alarm.time.tm_year = tm.tm_year; 327err= rtc_valid_tm(&alarm.time); 328if (err < 0) 329return err; 330rtc_tm_to_time(&alarm.time, &then); 331 332/* alarm may need to wrap into tomorrow */ 333if (then < now) { 334rtc_time_to_tm(now + 24 * 60 * 60, &tm); 335alarm.time.tm_mday = tm.tm_mday; 336alarm.time.tm_mon = tm.tm_mon; 337alarm.time.tm_year = tm.tm_year; 338} 339} 340 341return rtc_set_alarm(rtc, &alarm); 342 343case RTC_RD_TIME: 344mutex_unlock(&rtc->ops_lock); 345 346err = rtc_read_time(rtc, &tm); 347if (err < 0) 348return err; 349 350if (copy_to_user(uarg, &tm, sizeof(tm))) 351err = -EFAULT; 352return err; 353 354case RTC_SET_TIME: 355mutex_unlock(&rtc->ops_lock); 356 357if (copy_from_user(&tm, uarg, sizeof(tm))) 358return -EFAULT; 359 360return rtc_set_time(rtc, &tm); 361 362case RTC_PIE_ON: 363err = rtc_irq_set_state(rtc, NULL, 1); 364break; 365 366case RTC_PIE_OFF: 367err = rtc_irq_set_state(rtc, NULL, 0); 368break; 369 370case RTC_AIE_ON: 371mutex_unlock(&rtc->ops_lock); 372return rtc_alarm_irq_enable(rtc, 1); 373 374case RTC_AIE_OFF: 375mutex_unlock(&rtc->ops_lock); 376return rtc_alarm_irq_enable(rtc, 0); 377 378case RTC_UIE_ON: 379mutex_unlock(&rtc->ops_lock); 380return rtc_update_irq_enable(rtc, 1); 381 382case RTC_UIE_OFF: 383mutex_unlock(&rtc->ops_lock); 384return rtc_update_irq_enable(rtc, 0); 385 386case RTC_IRQP_SET: 387err = rtc_irq_set_freq(rtc, NULL, arg); 388break; 389 390case RTC_IRQP_READ: 391err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); 392break; 393 394 #if 0 395case RTC_EPOCH_SET: 396 #ifndef rtc_epoch 397/* 398* There were no RTC clocks before 1900. 399*/ 400if (arg < 1900) { 401err = -EINVAL; 402break; 403} 404rtc_epoch = arg; 405err = 0; 406 #endif 407break; 408 409case RTC_EPOCH_READ: 410err = put_user(rtc_epoch, (unsigned long __user *)uarg); 411break; 412 #endif 413case RTC_WKALM_SET: 414mutex_unlock(&rtc->ops_lock); 415if (copy_from_user(&alarm, uarg, sizeof(alarm))) 416return -EFAULT; 417 418return rtc_set_alarm(rtc, &alarm); 419 420case RTC_WKALM_RD: 421mutex_unlock(&rtc->ops_lock); 422err = rtc_read_alarm(rtc, &alarm); 423if (err < 0) 424return err; 425 426if (copy_to_user(uarg, &alarm, sizeof(alarm))) 427err = -EFAULT; 428return err; 429 430default: 431err = -ENOTTY; 432break; 433} 434 435 done: 436mutex_unlock(&rtc->ops_lock); 437return err; 438 }

这部分代码稍微有点长,但最主要的就两个switch,第一个switch主要是检测task是否具有操作的权限,注释也说了,对有些驱动是很有必要的。然后是尝试调用rtc设备驱动的ioctl操作,如果没有提供ioctl操作,则直接跳过。

根据相应的cmd,支持以下主要的操作:

RTC_ALM_READ: 读取闹钟时间 RTC_ALM_SET: 设置闹钟时间 RTC_RD_TIME: 读取rtc时间 RTC_SET_TIME: 设置rtc时间 RTC_WKALM_SET: 设置唤醒闹钟时间 RTC_WKALM_RD: 读取唤醒闹钟时间

为此,我写了给test程序,来读取rtc时间,代码如下:
#include #include #include #include #include #include #include #include #include static const char *rtc_name = "/dev/rtc0"; int main(void) { int fd; struct rtc_time tm; fd = open(rtc_name, O_RDWR); if (fd < 0) { exit(errno); }/* 注意:得到的是格林威治时间,北京时间还需要在此基础上加上8小时。 */ ioctl(fd, RTC_RD_TIME, &tm); printf("time is %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); close(fd); return 0; }


注意看rtc_dev_ioctl中,其中操作了interface.c提供的接口函数,比如rtc_read_alarm、rtc_set_alarm、rtc_read_time、rtc_set_time,而它们最终又调用的是rtc设备提供的操作,所以,最终还是操作的rtc时钟芯片,大家有兴趣可以去看一下这部分代码,这里就不在介绍了。


2. rtc sysfs接口
在class.c的初始化函数中,首先会调用rtc_sysfs_init函数,而里面只有一个操作,代码如下:
245 void __init rtc_sysfs_init(struct class *rtc_class) 246 { 247rtc_class->dev_attrs = rtc_attrs; 248 }

而rtc_attrs是定义的一些设备属性文件,定义如下:
118 static struct device_attribute rtc_attrs[] = { 119__ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), 120__ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), 121__ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), 122__ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), 123__ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, 124rtc_sysfs_set_max_user_freq), 125__ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), 126{ }, 127 };

里面提供了一些操作,例如,rtc_sysfs_show_name、rtc_sysfs_show_date、rtc_sysfs_show_time等等。可以在/sys/class/rtc/rtc0目录下操作一下,例如:
$ cat name rtc_cmos $ cat date 2014-09-02 $ cat time 08:03:57

在rtc_sysfs_show_date和rtc_sysfs_show_time函数里面还是调用的interface.c中的rtc_read_time接口函数。



那么这里呢还有个疑问,那就是这些属性文件并没有明显的调用device_create_file去创建它,只是在rtc_sysfs_init函数中赋值给了rtc_class,然后在rtc_device_register函数中将rtc_class赋值给了rtc设备,也就是每个rtc设备的class都是这里的rtc_class,但就是没有调用device_create_file函数去创建。后来也是在网上找到了结果,原来是在device_register函数中完成的,这部分的流程如下:

device_register()->device_add()->device_add_attrs()->device_add_attributes()->device_create_file()



而在rtc_device_register函数中还调用了rtc-sysfs.c中的rtc_sysfs_add_device函数,而该函数也没有做太多事情,也只是在sysfs下创建了dev_attr_wakealarm这个属性文件,代码如下:
224 void rtc_sysfs_add_device(struct rtc_device *rtc) 225 { 226int err; 227 228/* not all RTCs support both alarms and wakeup */ 229if (!rtc_does_wakealarm(rtc)) 230return; 231 232err = device_create_file(&rtc->dev, &dev_attr_wakealarm); 233if (err) 234dev_err(rtc->dev.parent, 235"failed to create alarm attribute, %d\n", err); 236 }

而它呢也提供了两个操作,rtc_sysfs_set_wakealarm和rtc_sysfs_show_wakealarm,这里也就不再去细看了。

3. rtc proc接口
在rtc_device_register函数中最后调用了proc中的rtc_proc_add_device函数,代码如下:
106 void rtc_proc_add_device(struct rtc_device *rtc) 107 { 108if (rtc->id == 0) 109proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); 110 }

将创建/proc/driver/rtc这个文件,并且关联了rtc_proc_fops操作。
而rtc_proc_del_device函数则是删除这个proc文件。
112 void rtc_proc_del_device(struct rtc_device *rtc) 113 { 114if (rtc->id == 0) 115remove_proc_entry("driver/rtc", NULL); 116 }


而其中的rtc_proc_show代码如下:
22 static int rtc_proc_show(struct seq_file *seq, void *offset) 23 { 24int err; 25struct rtc_device *rtc = seq->private; 26const struct rtc_class_ops *ops = rtc->ops; 27struct rtc_wkalrm alrm; 28struct rtc_time tm; 29 30err = rtc_read_time(rtc, &tm); 31if (err == 0) { 32seq_printf(seq, 33"rtc_time\t: %02d:%02d:%02d\n" 34"rtc_date\t: %04d-%02d-%02d\n", 35tm.tm_hour, tm.tm_min, tm.tm_sec, 36tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); 37} 38 39err = rtc_read_alarm(rtc, &alrm); 40if (err == 0) { 41seq_printf(seq, "alrm_time\t: "); 42if ((unsigned int)alrm.time.tm_hour <= 24) 43seq_printf(seq, "%02d:", alrm.time.tm_hour); 44else 45seq_printf(seq, "**:"); 46if ((unsigned int)alrm.time.tm_min <= 59) 47seq_printf(seq, "%02d:", alrm.time.tm_min); 48else 49seq_printf(seq, "**:"); 50if ((unsigned int)alrm.time.tm_sec <= 59) 51seq_printf(seq, "%02d\n", alrm.time.tm_sec); 52else 53seq_printf(seq, "**\n"); 54 55seq_printf(seq, "alrm_date\t: "); 56if ((unsigned int)alrm.time.tm_year <= 200) 57seq_printf(seq, "%04d-", alrm.time.tm_year + 1900); 58else 59seq_printf(seq, "****-"); 60if ((unsigned int)alrm.time.tm_mon <= 11) 61seq_printf(seq, "%02d-", alrm.time.tm_mon + 1); 62else 63seq_printf(seq, "**-"); 64if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31) 65seq_printf(seq, "%02d\n", alrm.time.tm_mday); 66else 67seq_printf(seq, "**\n"); 68seq_printf(seq, "alarm_IRQ\t: %s\n", 69alrm.enabled ? "yes" : "no"); 70seq_printf(seq, "alrm_pending\t: %s\n", 71alrm.pending ? "yes" : "no"); 72} 73 74seq_printf(seq, "24hr\t\t: yes\n"); 75 76if (ops->proc) 77ops->proc(rtc->dev.parent, seq); 78 79return 0; 80 }

主要是调用rtc_read_time和rtc_read_alarm函数,然后信息显示给用户,例如:
$ cat rtc rtc_time : 09:04:40 rtc_date : 2014-09-02 alrm_time : 01:30:08 alrm_date : 2014-09-03 alarm_IRQ : no alrm_pending : no update IRQ enabled : no periodic IRQ enabled : no periodic IRQ frequency : 1024 max user IRQ frequency : 64 24hr: yes periodic_IRQ : no update_IRQ : no HPET_emulated : yes BCD: yes DST_enable : no periodic_freq : 1024 batt_status : okay




至此,rtc驱动核心模块大概也浏览完了,rtc主要为用户提供了三个接口供其操作,char device、sysfs、proc。



那么再以一个具体的例子来看一下rtc驱动,以s3c2440为例。
首先是模块的初始化部分,代码如下:
515 static struct platform_driver s3c2410_rtc_driver = { 516.probe= s3c_rtc_probe, 517.remove= __devexit_p(s3c_rtc_remove), 518.suspend= s3c_rtc_suspend, 519.resume= s3c_rtc_resume, 520.driver= { 521.name= "s3c2410-rtc", 522.owner= THIS_MODULE, 523}, 524 }; 525 526 static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n"; 527 528 static int __init s3c_rtc_init(void) 529 { 530printk(banner); 531return platform_driver_register(&s3c2410_rtc_driver); 532 } 533 534 static void __exit s3c_rtc_exit(void) 535 { 536platform_driver_unregister(&s3c2410_rtc_driver); 537 } 538 539 module_init(s3c_rtc_init); 540 module_exit(s3c_rtc_exit);


platform_device定义如下:
308 /* RTC */ 309 310 static struct resource s3c_rtc_resource[] = { 311[0] = { 312.start = S3C24XX_PA_RTC, 313.end= S3C24XX_PA_RTC + 0xff, 314.flags = IORESOURCE_MEM, 315}, 316[1] = { 317.start = IRQ_RTC, 318.end= IRQ_RTC, 319.flags = IORESOURCE_IRQ, 320}, 321[2] = { 322.start = IRQ_TICK, 323.end= IRQ_TICK, 324.flags = IORESOURCE_IRQ 325} 326 }; 327 328 struct platform_device s3c_device_rtc = { 329.name= "s3c2410-rtc", 330.id= -1, 331.num_resources= ARRAY_SIZE(s3c_rtc_resource), 332.resource= s3c_rtc_resource, 333 }; 334 335 EXPORT_SYMBOL(s3c_device_rtc);


再来看probe函数s3c_rtc_probe:
402 static int __devinit s3c_rtc_probe(struct platform_device *pdev) 403 { 404struct rtc_device *rtc; 405struct resource *res; 406int ret; 407 408pr_debug("%s: probe=%p\n", __func__, pdev); 409 410/* find the IRQs */ 411 412s3c_rtc_tickno = platform_get_irq(pdev, 1); 413if (s3c_rtc_tickno < 0) { 414dev_err(&pdev->dev, "no irq for rtc tick\n"); 415return -ENOENT; 416} 417 418s3c_rtc_alarmno = platform_get_irq(pdev, 0); 419if (s3c_rtc_alarmno < 0) { 420dev_err(&pdev->dev, "no irq for alarm\n"); 421return -ENOENT; 422} 423 424pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", 425s3c_rtc_tickno, s3c_rtc_alarmno); 426 427/* get the memory region */ 428 429res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 430if (res == NULL) { 431dev_err(&pdev->dev, "failed to get memory region resource\n"); 432return -ENOENT; 433} 434 435s3c_rtc_mem = request_mem_region(res->start, 436res->end-res->start+1, 437pdev->name); 438 439if (s3c_rtc_mem == NULL) { 440dev_err(&pdev->dev, "failed to reserve memory region\n"); 441ret = -ENOENT; 442goto err_nores; 443} 444 445s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); 446if (s3c_rtc_base == NULL) { 447dev_err(&pdev->dev, "failed ioremap()\n"); 448ret = -EINVAL; 449goto err_nomap; 450} 451 452/* check to see if everything is setup correctly */ 453 454s3c_rtc_enable(pdev, 1); 455 456pr_debug("s3c2410_rtc: RTCCON=%02x\n", 457readb(s3c_rtc_base + S3C2410_RTCCON)); 458 459s3c_rtc_setfreq(&pdev->dev, 1); 460 461device_init_wakeup(&pdev->dev, 1); 462 463/* register RTC and exit */ 464 465rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, 466THIS_MODULE); 467 468if (IS_ERR(rtc)) { 469dev_err(&pdev->dev, "cannot attach rtc\n"); 470ret = PTR_ERR(rtc); 471goto err_nortc; 472} 473 474rtc->max_user_freq = 128; 475 476platform_set_drvdata(pdev, rtc); 477return 0; 478 479err_nortc: 480s3c_rtc_enable(pdev, 0); 481iounmap(s3c_rtc_base); 482 483err_nomap: 484release_resource(s3c_rtc_mem); 485 486err_nores: 487return ret; 488 }

在函数开始部分都是同平台设备相关的,例如,获取平台设备资源,进行io内存映射等等。454行,调用s3c_rtc_enable函数,s3c_rtc_enable用于使能rtc模块,代码如下:
345 static void s3c_rtc_enable(struct platform_device *pdev, int en) 346 { 347void __iomem *base = s3c_rtc_base; 348unsigned int tmp; 349 350if (s3c_rtc_base == NULL) 351return; 352 353if (!en) { 354tmp = readb(base + S3C2410_RTCCON); 355writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); 356 357tmp = readb(base + S3C2410_TICNT); 358writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); 359} else { 360/* re-enable the device, and check it is ok */ 361 362if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ 363dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); 364 365tmp = readb(base + S3C2410_RTCCON); 366writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON); 367} 368 369if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){ 370dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); 371 372tmp = readb(base + S3C2410_RTCCON); 373writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON); 374} 375 376if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){ 377dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); 378 379tmp = readb(base + S3C2410_RTCCON); 380writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON); 381} 382} 383 }

代码根据参数en分为两个部分,使能和禁止,如果是禁止rtc模块,则将RTCCON寄存器的RTCEN位置0,同时将TICNT寄存器的ENABLE位置0。如果是使能rtc模块,那么自然是将RTCON寄存器的RTCEN位置1,同时将该寄存器的CNTSEL、CLKRST位置0。所以该函数就是使能和禁止rtc模块的操作的函数。

还是回到probe函数中,然后是调用s3c_rtc_setfreq函数,函数如下:
93 static int s3c_rtc_setfreq(struct device *dev, int freq) 94 { 95unsigned int tmp; 96 97if (!is_power_of_2(freq)) 98return -EINVAL; 99 100spin_lock_irq(&s3c_rtc_pie_lock); 101 102tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; 103tmp |= (128 / freq)-1; 104 105writeb(tmp, s3c_rtc_base + S3C2410_TICNT); 106spin_unlock_irq(&s3c_rtc_pie_lock); 107 108return 0; 109 }

首先检测freq这个值是否合法,然后读取TICNT寄存器,并将低7位清0,然后计算得tick count值,并将该值写入寄存器TICNT中。

【Linux rtc驱动模块分析】在probe函数中,最后调用rtc_device_register去完成rtc设备的注册,注意注册时提供了s3c_rtcops这个参数,它就是rtc的ops操作。



那么接下来就来看看s3c_rtcops:
333 static const struct rtc_class_ops s3c_rtcops = { 334.open= s3c_rtc_open, 335.release= s3c_rtc_release, 336.read_time= s3c_rtc_gettime, 337.set_time= s3c_rtc_settime, 338.read_alarm= s3c_rtc_getalarm, 339.set_alarm= s3c_rtc_setalarm, 340.irq_set_freq= s3c_rtc_setfreq, 341.irq_set_state= s3c_rtc_setpie, 342.proc= s3c_rtc_proc, 343 };


首先看s3c_rtc_open:
292 static int s3c_rtc_open(struct device *dev) 293 { 294struct platform_device *pdev = to_platform_device(dev); 295struct rtc_device *rtc_dev = platform_get_drvdata(pdev); 296int ret; 297 298ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq, 299IRQF_DISABLED,"s3c2410-rtc alarm", rtc_dev); 300 301if (ret) { 302dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); 303return ret; 304} 305 306ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, 307IRQF_DISABLED,"s3c2410-rtc tick", rtc_dev); 308 309if (ret) { 310dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); 311goto tick_err; 312} 313 314return ret; 315 316tick_err: 317free_irq(s3c_rtc_alarmno, rtc_dev); 318return ret; 319 }

在open函数中,主要申请了两个中断s3c_rtc_alarmirq和s3c_rtc_tickirq。而在release函数中,则是释放这两个申请:
321 static void s3c_rtc_release(struct device *dev) 322 { 323struct platform_device *pdev = to_platform_device(dev); 324struct rtc_device *rtc_dev = platform_get_drvdata(pdev); 325 326/* do not clear AIE here, it may be needed for wake */ 327 328s3c_rtc_setpie(dev, 0); 329free_irq(s3c_rtc_alarmno, rtc_dev); 330free_irq(s3c_rtc_tickno, rtc_dev); 331 }


再来看gettime操作:
113 static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 114 { 115unsigned int have_retried = 0; 116void __iomem *base = s3c_rtc_base; 117 118retry_get_time: 119rtc_tm->tm_min= readb(base + S3C2410_RTCMIN); 120rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); 121rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); 122rtc_tm->tm_mon= readb(base + S3C2410_RTCMON); 123rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); 124rtc_tm->tm_sec= readb(base + S3C2410_RTCSEC); 125 126/* the only way to work out wether the system was mid-update 127* when we read it is to check the second counter, and if it 128* is zero, then we re-try the entire read 129*/ 130 131if (rtc_tm->tm_sec == 0 && !have_retried) { 132have_retried = 1; 133goto retry_get_time; 134} 135 136pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", 137rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, 138rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); 139 140rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); 141rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); 142rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); 143rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); 144rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); 145rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); 146 147rtc_tm->tm_year += 100; 148rtc_tm->tm_mon -= 1; 149 150return 0; 151 }

主要是读取2440的rtc模块的寄存器,例如:BCDSEC、BCDMIN、BCDHOUR、BDCDATE、BCDMON、BCDYEAR等。注意读取出来的数据是BCD码,需要调用bcd2bin将BCD码转换成二进制码。

settime代码如下:
153 static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) 154 { 155void __iomem *base = s3c_rtc_base; 156int year = tm->tm_year - 100; 157 158pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n", 159tm->tm_year, tm->tm_mon, tm->tm_mday, 160tm->tm_hour, tm->tm_min, tm->tm_sec); 161 162/* we get around y2k by simply not supporting it */ 163 164if (year < 0 || year >= 100) { 165dev_err(dev, "rtc only supports 100 years\n"); 166return -EINVAL; 167} 168 169writeb(bin2bcd(tm->tm_sec),base + S3C2410_RTCSEC); 170writeb(bin2bcd(tm->tm_min),base + S3C2410_RTCMIN); 171writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); 172writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); 173writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); 174writeb(bin2bcd(year), base + S3C2410_RTCYEAR); 175 176return 0; 177 }

将rtc_time中的tm_year、tm_mon、tm_mday、tm_hour、tm_min和tm_sec写入到相应的寄存器中,同样需要将二进制码转换成BCD码。

再来看getalarm和setalarm,s3c_rtc_getalarm代码如下:
179 static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) 180 { 181struct rtc_time *alm_tm = &alrm->time; 182void __iomem *base = s3c_rtc_base; 183unsigned int alm_en; 184 185alm_tm->tm_sec= readb(base + S3C2410_ALMSEC); 186alm_tm->tm_min= readb(base + S3C2410_ALMMIN); 187alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); 188alm_tm->tm_mon= readb(base + S3C2410_ALMMON); 189alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); 190alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); 191 192alm_en = readb(base + S3C2410_RTCALM); 193 194alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; 195 196pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", 197alm_en, 198alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, 199alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); 200 201 202/* decode the alarm enable field */ 203 204if (alm_en & S3C2410_RTCALM_SECEN) 205alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); 206else 207alm_tm->tm_sec = 0xff; 208 209if (alm_en & S3C2410_RTCALM_MINEN) 210alm_tm->tm_min = bcd2bin(alm_tm->tm_min); 211else 212alm_tm->tm_min = 0xff; 213 214if (alm_en & S3C2410_RTCALM_HOUREN) 215alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); 216else 217alm_tm->tm_hour = 0xff; 218 219if (alm_en & S3C2410_RTCALM_DAYEN) 220alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); 221else 222alm_tm->tm_mday = 0xff; 223 224if (alm_en & S3C2410_RTCALM_MONEN) { 225alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); 226alm_tm->tm_mon -= 1; 227} else { 228alm_tm->tm_mon = 0xff; 229} 230 231if (alm_en & S3C2410_RTCALM_YEAREN) 232alm_tm->tm_year = bcd2bin(alm_tm->tm_year); 233else 234alm_tm->tm_year = 0xffff; 235 236return 0; 237 }

首先读取alarm相关寄存器,例如:ALMSEC、ALMMIN、ALMHOUR、ALMDATE、ALMMON、ALMYEAR,然后再读取RTCALM寄存器,它是alarm的使能寄存器,根据RTCALM寄存器相关设置来禁止无效的alarm设置信息。

s3c_rtc_setalarm代码如下:
239 static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 240 { 241struct rtc_time *tm = &alrm->time; 242void __iomem *base = s3c_rtc_base; 243unsigned int alrm_en; 244 245pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", 246alrm->enabled, 247tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, 248tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); 249 250 251alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; 252writeb(0x00, base + S3C2410_RTCALM); 253 254if (tm->tm_sec < 60 && tm->tm_sec >= 0) { 255alrm_en |= S3C2410_RTCALM_SECEN; 256writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); 257} 258 259if (tm->tm_min < 60 && tm->tm_min >= 0) { 260alrm_en |= S3C2410_RTCALM_MINEN; 261writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); 262} 263 264if (tm->tm_hour < 24 && tm->tm_hour >= 0) { 265alrm_en |= S3C2410_RTCALM_HOUREN; 266writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); 267} 268 269pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); 270 271writeb(alrm_en, base + S3C2410_RTCALM); 272 273s3c_rtc_setaie(alrm->enabled); 274 275if (alrm->enabled) 276enable_irq_wake(s3c_rtc_alarmno); 277else 278disable_irq_wake(s3c_rtc_alarmno); 279 280return 0; 281 }

首先是读取RTCALM寄存器,最高位的使能位保存在变量alrm_en中,然后将RTCALM寄存器全置0,然后判断rtc_time中的tm_sec、tm_min、tm_hour值是否有效,如果是有效的值,设置相应的寄存器,并使能相关的alarm使能位,最后将alrm_en这个变量写入到寄存器RTCALM中。
再调用s3c_rtc_setaie函数,这个函数是根据alrm->enabled来设置是否打开alarm的使能,代码如下:
61 static void s3c_rtc_setaie(int to) 62 { 63unsigned int tmp; 64 65pr_debug("%s: aie=%d\n", __func__, to); 66 67tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; 68 69if (to) 70tmp |= S3C2410_RTCALM_ALMEN; 71 72writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); 73 }

如果to是1,表示使能alarm,如果是0,表示禁止alarm。
最后根据alrm->enabled这个值来打开alarm的中断。至此,整个驱动程序也就分析完成了。

    推荐阅读