内核版本: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的中断。至此,整个驱动程序也就分析完成了。