Linux rtc驱动模块分析

内核版本:linux-2.6.32

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

211 static int __init rtc_init(void)
212 {
213         rtc_class = class_create(THIS_MODULE, "rtc");
214         if (IS_ERR(rtc_class)) {
215                 printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
216                 return PTR_ERR(rtc_class);
217         }
218         rtc_class->suspend = rtc_suspend;
219         rtc_class->resume = rtc_resume;
220         rtc_dev_init();
221         rtc_sysfs_init(rtc_class);
222         return 0;
223 }
224 
225 static void __exit rtc_exit(void)
226 {
227         rtc_dev_exit();
228         class_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,
114                                         const struct rtc_class_ops *ops,
115                                         struct module *owner)
116 {
117         struct rtc_device *rtc;
118         int id, err;
119 
120         if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
121                 err = -ENOMEM;
122                 goto exit;
123         }
124 
125 
126         mutex_lock(&idr_lock);
127         err = idr_get_new(&rtc_idr, NULL, &id);
128         mutex_unlock(&idr_lock);
129 
130         if (err < 0)
131                 goto exit;
132 
133         id = id & MAX_ID_MASK;
134 
135         rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
136         if (rtc == NULL) {
137                 err = -ENOMEM;
138                 goto exit_idr;
139         }
140 
141         rtc->id = id;
142         rtc->ops = ops;
143         rtc->owner = owner;
144         rtc->max_user_freq = 64;
145         rtc->dev.parent = dev;
146         rtc->dev.class = rtc_class;
147         rtc->dev.release = rtc_device_release;
148 
149         mutex_init(&rtc->ops_lock);
150         spin_lock_init(&rtc->irq_lock);
151         spin_lock_init(&rtc->irq_task_lock);
152         init_waitqueue_head(&rtc->irq_queue);
153 
154         strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
155         dev_set_name(&rtc->dev, "rtc%d", id);
156 
157         rtc_dev_prepare(rtc);
158 
159         err = device_register(&rtc->dev);
160         if (err)
161                 goto exit_kfree;
162 
163         rtc_dev_add_device(rtc);
164         rtc_sysfs_add_device(rtc);
165         rtc_proc_add_device(rtc);
166 
167         dev_info(dev, "rtc core: registered %s as %s\n",
168                         rtc->name, dev_name(&rtc->dev));
169 
170         return rtc;
171 
172 exit_kfree:
173         kfree(rtc);
174 
175 exit_idr:
176         mutex_lock(&idr_lock);
177         idr_remove(&rtc_idr, id);
178         mutex_unlock(&idr_lock);
179 
180 exit:
181         dev_err(dev, "rtc core: unable to register %s, err = %d\n",
182                         name, err);
183         return 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 {
486         if (!rtc_devt)
487                 return;
488 
489         if (rtc->id >= RTC_DEV_MAX) {
490                 pr_debug("%s: too many RTC devices\n", rtc->name);
491                 return;
492         }
493 
494         rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
495 
496 #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
497         INIT_WORK(&rtc->uie_task, rtc_uie_task);
498         setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
499 #endif
500 
501         cdev_init(&rtc->char_dev, &rtc_dev_fops);
502         rtc->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 {
507         if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
508                 printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
509                         rtc->name, MAJOR(rtc_devt), rtc->id);
510         else
511                 pr_debug("%s: dev (%d:%d)\n", rtc->name,
512                         MAJOR(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 {
195         if (get_device(&rtc->dev) != NULL) {
196                 mutex_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                  */
200                 rtc_sysfs_del_device(rtc);
201                 rtc_dev_del_device(rtc);
202                 rtc_proc_del_device(rtc);
203                 device_unregister(&rtc->dev);
204                 rtc->ops = NULL;
205                 mutex_unlock(&rtc->ops_lock);
206                 put_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 {
523         int err;
524 
525         err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
526         if (err < 0)
527                 printk(KERN_ERR "%s: failed to allocate char dev region\n",
528                         __FILE__);
529 }
可见,该函数只是申请了一个字符设备号。而与之对应的rtc_dev_exit肯定是注销申请的设备号,代码如下:
531 void __exit rtc_dev_exit(void)
532 {
533         if (rtc_devt)
534                 unregister_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 {
 25         int err;
 26         struct rtc_device *rtc = container_of(inode->i_cdev,
 27                                         struct rtc_device, char_dev);
 28         const struct rtc_class_ops *ops = rtc->ops;
 29 
 30         if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
 31                 return -EBUSY;
 32 
 33         file->private_data = rtc;
 34 
 35         err = ops->open ? ops->open(rtc->dev.parent) : 0;
 36         if (err == 0) {
 37                 spin_lock_irq(&rtc->irq_lock);
 38                 rtc->irq_data = 0;
 39                 spin_unlock_irq(&rtc->irq_lock);
 40 
 41                 return 0;
 42         }
 43 
 44         /* something has gone wrong */
 45         clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
 46         return 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 {
448         struct 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 */
460         rtc_dev_ioctl(file, RTC_UIE_OFF, 0);
461         rtc_update_irq_enable(rtc, 0);
462         rtc_irq_set_state(rtc, NULL, 0);
463 
464         if (rtc->ops->release)
465                 rtc->ops->release(rtc->dev.parent);
466 
467         clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
468         return 0;
469 }

再来看read操作:
149 static ssize_t
150 rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
151 {
152         struct rtc_device *rtc = file->private_data;
153 
154         DECLARE_WAITQUEUE(wait, current);
155         unsigned long data;
156         ssize_t ret;
157 
158         if (count != sizeof(unsigned int) && count < sizeof(unsigned long))
159                 return -EINVAL;
160 
161         add_wait_queue(&rtc->irq_queue, &wait);
162         do {
163                 __set_current_state(TASK_INTERRUPTIBLE);
164 
165                 spin_lock_irq(&rtc->irq_lock);
166                 data = rtc->irq_data;
167                 rtc->irq_data = 0;
168                 spin_unlock_irq(&rtc->irq_lock);
169 
170                 if (data != 0) {
171                         ret = 0;
172                         break;
173                 }
174                 if (file->f_flags & O_NONBLOCK) {
175                         ret = -EAGAIN;
176                         break;
177                 }
178                 if (signal_pending(current)) {
179                         ret = -ERESTARTSYS;
180                         break;
181                 }
182                 schedule();
183         } while (1);
184         set_current_state(TASK_RUNNING);
185         remove_wait_queue(&rtc->irq_queue, &wait);
186 
187         if (ret == 0) {
188                 /* Check for any data updates */
189                 if (rtc->ops->read_callback)
190                         data = rtc->ops->read_callback(rtc->dev.parent,
191                                                        data);
192 
193                 if (sizeof(int) != sizeof(long) &&
194                     count == sizeof(unsigned int))
195                         ret = put_user(data, (unsigned int __user *)buf) ?:
196                                 sizeof(unsigned int);
197                 else
198                         ret = put_user(data, (unsigned long __user *)buf) ?:
199                                 sizeof(unsigned long);
200         }
201         return ret;
202 }
调用read函数时,可能会没有数据供应用程序读取,所以提供了等待队列,在没有数据读取时阻塞read操作。最后调用的是rtc设备的read_callback方法,最后将读取到的数据返回给应用程序。

再来看ioctl操作,代码如下:

216 static long rtc_dev_ioctl(struct file *file,
217                 unsigned int cmd, unsigned long arg)
218 {
219         int err = 0;
220         struct rtc_device *rtc = file->private_data;
221         const struct rtc_class_ops *ops = rtc->ops;
222         struct rtc_time tm;
223         struct rtc_wkalrm alarm;
224         void __user *uarg = (void __user *) arg;
225 
226         err = mutex_lock_interruptible(&rtc->ops_lock);
227         if (err)
228                 return 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          */
234         switch (cmd) {
235         case RTC_EPOCH_SET:
236         case RTC_SET_TIME:
237                 if (!capable(CAP_SYS_TIME))
238                         err = -EACCES;
239                 break;
240 
241         case RTC_IRQP_SET:
242                 if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
243                         err = -EACCES;
244                 break;
245 
246         case RTC_PIE_ON:
247                 if (rtc->irq_freq > rtc->max_user_freq &&
248                                 !capable(CAP_SYS_RESOURCE))
249                         err = -EACCES;
250                 break;
251         }
252 
253         if (err)
254                 goto done;
255 
256         /* try the driver's ioctl interface */
257         if (ops->ioctl) {
258                 err = ops->ioctl(rtc->dev.parent, cmd, arg);
259                 if (err != -ENOIOCTLCMD) {
260                         mutex_unlock(&rtc->ops_lock);
261                         return 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 
279         switch (cmd) {
280         case RTC_ALM_READ:
281                 mutex_unlock(&rtc->ops_lock);
282 
283                 err = rtc_read_alarm(rtc, &alarm);
284                 if (err < 0)
285                         return err;
286 
287                 if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
288                         err = -EFAULT;
289                 return err;
290 
291         case RTC_ALM_SET:
292                 mutex_unlock(&rtc->ops_lock);
293 
294                 if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
295                         return -EFAULT;
296 
297                 alarm.enabled = 0;
298                 alarm.pending = 0;
299                 alarm.time.tm_wday = -1;
300                 alarm.time.tm_yday = -1;
301                 alarm.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                 {
317                         unsigned long now, then;
318 
319                         err = rtc_read_time(rtc, &tm);
320                         if (err < 0)
321                                 return err;
322                         rtc_tm_to_time(&tm, &now);
323 
324                         alarm.time.tm_mday = tm.tm_mday;
325                         alarm.time.tm_mon = tm.tm_mon;
326                         alarm.time.tm_year = tm.tm_year;
327                         err  = rtc_valid_tm(&alarm.time);
328                         if (err < 0)
329                                 return err;
330                         rtc_tm_to_time(&alarm.time, &then);
331 
332                         /* alarm may need to wrap into tomorrow */
333                         if (then < now) {
334                                 rtc_time_to_tm(now + 24 * 60 * 60, &tm);
335                                 alarm.time.tm_mday = tm.tm_mday;
336                                 alarm.time.tm_mon = tm.tm_mon;
337                                 alarm.time.tm_year = tm.tm_year;
338                         }
339                 }
340 
341                 return rtc_set_alarm(rtc, &alarm);
342 
343         case RTC_RD_TIME:
344                 mutex_unlock(&rtc->ops_lock);
345 
346                 err = rtc_read_time(rtc, &tm);
347                 if (err < 0)
348                         return err;
349 
350                 if (copy_to_user(uarg, &tm, sizeof(tm)))
351                         err = -EFAULT;
352                 return err;
353 
354         case RTC_SET_TIME:
355                 mutex_unlock(&rtc->ops_lock);
356 
357                 if (copy_from_user(&tm, uarg, sizeof(tm)))
358                         return -EFAULT;
359 
360                 return rtc_set_time(rtc, &tm);
361 
362         case RTC_PIE_ON:
363                 err = rtc_irq_set_state(rtc, NULL, 1);
364                 break;
365 
366         case RTC_PIE_OFF:
367                 err = rtc_irq_set_state(rtc, NULL, 0);
368                 break;
369 
370         case RTC_AIE_ON:
371                 mutex_unlock(&rtc->ops_lock);
372                 return rtc_alarm_irq_enable(rtc, 1);
373 
374         case RTC_AIE_OFF:
375                 mutex_unlock(&rtc->ops_lock);
376                 return rtc_alarm_irq_enable(rtc, 0);
377 
378         case RTC_UIE_ON:
379                 mutex_unlock(&rtc->ops_lock);
380                 return rtc_update_irq_enable(rtc, 1);
381 
382         case RTC_UIE_OFF:
383                 mutex_unlock(&rtc->ops_lock);
384                 return rtc_update_irq_enable(rtc, 0);
385 
386         case RTC_IRQP_SET:
387                 err = rtc_irq_set_freq(rtc, NULL, arg);
388                 break;
389 
390         case RTC_IRQP_READ:
391                 err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
392                 break;
393 
394 #if 0
395         case RTC_EPOCH_SET:
396 #ifndef rtc_epoch
397                 /*
398                  * There were no RTC clocks before 1900.
399                  */
400                 if (arg < 1900) {
401                         err = -EINVAL;
402                         break;
403                 }
404                 rtc_epoch = arg;
405                 err = 0;
406 #endif
407                 break;
408 
409         case RTC_EPOCH_READ:
410                 err = put_user(rtc_epoch, (unsigned long __user *)uarg);
411                 break;
412 #endif
413         case RTC_WKALM_SET:
414                 mutex_unlock(&rtc->ops_lock);
415                 if (copy_from_user(&alarm, uarg, sizeof(alarm)))
416                         return -EFAULT;
417 
418                 return rtc_set_alarm(rtc, &alarm);
419 
420         case RTC_WKALM_RD:
421                 mutex_unlock(&rtc->ops_lock);
422                 err = rtc_read_alarm(rtc, &alarm);
423                 if (err < 0)
424                         return err;
425 
426                 if (copy_to_user(uarg, &alarm, sizeof(alarm)))
427                         err = -EFAULT;
428                 return err;
429 
430         default:
431                 err = -ENOTTY;
432                 break;
433         }
434 
435 done:
436         mutex_unlock(&rtc->ops_lock);
437         return 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 <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#include <errno.h>
#include <stdlib.h>

#include <linux/rtc.h>

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 {
247         rtc_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,
124                         rtc_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 {
226         int err;
227 
228         /* not all RTCs support both alarms and wakeup */
229         if (!rtc_does_wakealarm(rtc))
230                 return;
231 
232         err = device_create_file(&rtc->dev, &dev_attr_wakealarm);
233         if (err)
234                 dev_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 {
108         if (rtc->id == 0)
109                 proc_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 {
114         if (rtc->id == 0)
115                 remove_proc_entry("driver/rtc", NULL);
116 }

而其中的rtc_proc_show代码如下:
 22 static int rtc_proc_show(struct seq_file *seq, void *offset)
 23 {
 24         int err;
 25         struct rtc_device *rtc = seq->private;
 26         const struct rtc_class_ops *ops = rtc->ops;
 27         struct rtc_wkalrm alrm;
 28         struct rtc_time tm;
 29 
 30         err = rtc_read_time(rtc, &tm);
 31         if (err == 0) {
 32                 seq_printf(seq,
 33                         "rtc_time\t: %02d:%02d:%02d\n"
 34                         "rtc_date\t: %04d-%02d-%02d\n",
 35                         tm.tm_hour, tm.tm_min, tm.tm_sec,
 36                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
 37         }
 38 
 39         err = rtc_read_alarm(rtc, &alrm);
 40         if (err == 0) {
 41                 seq_printf(seq, "alrm_time\t: ");
 42                 if ((unsigned int)alrm.time.tm_hour <= 24)
 43                         seq_printf(seq, "%02d:", alrm.time.tm_hour);
 44                 else
 45                         seq_printf(seq, "**:");
 46                 if ((unsigned int)alrm.time.tm_min <= 59)
 47                         seq_printf(seq, "%02d:", alrm.time.tm_min);
 48                 else
 49                         seq_printf(seq, "**:");
 50                 if ((unsigned int)alrm.time.tm_sec <= 59)
 51                         seq_printf(seq, "%02d\n", alrm.time.tm_sec);
 52                 else
 53                         seq_printf(seq, "**\n");
 54 
 55                 seq_printf(seq, "alrm_date\t: ");
 56                 if ((unsigned int)alrm.time.tm_year <= 200)
 57                         seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
 58                 else
 59                         seq_printf(seq, "****-");
 60                 if ((unsigned int)alrm.time.tm_mon <= 11)
 61                         seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
 62                 else
 63                         seq_printf(seq, "**-");
 64                 if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
 65                         seq_printf(seq, "%02d\n", alrm.time.tm_mday);
 66                 else
 67                         seq_printf(seq, "**\n");
 68                 seq_printf(seq, "alarm_IRQ\t: %s\n",
 69                                 alrm.enabled ? "yes" : "no");
 70                 seq_printf(seq, "alrm_pending\t: %s\n",
 71                                 alrm.pending ? "yes" : "no");
 72         }
 73 
 74         seq_printf(seq, "24hr\t\t: yes\n");
 75 
 76         if (ops->proc)
 77                 ops->proc(rtc->dev.parent, seq);
 78 
 79         return 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 {
530         printk(banner);
531         return platform_driver_register(&s3c2410_rtc_driver);
532 }
533 
534 static void __exit s3c_rtc_exit(void)
535 {
536         platform_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 {
404         struct rtc_device *rtc;
405         struct resource *res;
406         int ret;
407 
408         pr_debug("%s: probe=%p\n", __func__, pdev);
409 
410         /* find the IRQs */
411 
412         s3c_rtc_tickno = platform_get_irq(pdev, 1);
413         if (s3c_rtc_tickno < 0) {
414                 dev_err(&pdev->dev, "no irq for rtc tick\n");
415                 return -ENOENT;
416         }
417 
418         s3c_rtc_alarmno = platform_get_irq(pdev, 0);
419         if (s3c_rtc_alarmno < 0) {
420                 dev_err(&pdev->dev, "no irq for alarm\n");
421                 return -ENOENT;
422         }
423 
424         pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
425                  s3c_rtc_tickno, s3c_rtc_alarmno);
426 
427         /* get the memory region */
428 
429         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
430         if (res == NULL) {
431                 dev_err(&pdev->dev, "failed to get memory region resource\n");
432                 return -ENOENT;
433         }
434 
435         s3c_rtc_mem = request_mem_region(res->start,
436                                          res->end-res->start+1,
437                                          pdev->name);
438 
439         if (s3c_rtc_mem == NULL) {
440                 dev_err(&pdev->dev, "failed to reserve memory region\n");
441                 ret = -ENOENT;
442                 goto err_nores;
443         }
444 
445         s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
446         if (s3c_rtc_base == NULL) {
447                 dev_err(&pdev->dev, "failed ioremap()\n");
448                 ret = -EINVAL;
449                 goto err_nomap;
450         }
451 
452         /* check to see if everything is setup correctly */
453 
454         s3c_rtc_enable(pdev, 1);
455 
456         pr_debug("s3c2410_rtc: RTCCON=%02x\n",
457                  readb(s3c_rtc_base + S3C2410_RTCCON));
458 
459         s3c_rtc_setfreq(&pdev->dev, 1);
460 
461         device_init_wakeup(&pdev->dev, 1);
462 
463         /* register RTC and exit */
464 
465         rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
466                                   THIS_MODULE);
467 
468         if (IS_ERR(rtc)) {
469                 dev_err(&pdev->dev, "cannot attach rtc\n");
470                 ret = PTR_ERR(rtc);
471                 goto err_nortc;
472         }
473 
474         rtc->max_user_freq = 128;
475 
476         platform_set_drvdata(pdev, rtc);
477         return 0;
478 
479  err_nortc:
480         s3c_rtc_enable(pdev, 0);
481         iounmap(s3c_rtc_base);
482 
483  err_nomap:
484         release_resource(s3c_rtc_mem);
485 
486  err_nores:
487         return ret;
488 }
在函数开始部分都是同平台设备相关的,例如,获取平台设备资源,进行io内存映射等等。454行,调用s3c_rtc_enable函数,s3c_rtc_enable用于使能rtc模块,代码如下:
345 static void s3c_rtc_enable(struct platform_device *pdev, int en)
346 {
347         void __iomem *base = s3c_rtc_base;
348         unsigned int tmp;
349 
350         if (s3c_rtc_base == NULL)
351                 return;
352 
353         if (!en) {
354                 tmp = readb(base + S3C2410_RTCCON);
355                 writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);
356 
357                 tmp = readb(base + S3C2410_TICNT);
358                 writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);
359         } else {
360                 /* re-enable the device, and check it is ok */
361 
362                 if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
363                         dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
364 
365                         tmp = readb(base + S3C2410_RTCCON);
366                         writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
367                 }
368 
369                 if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
370                         dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
371 
372                         tmp = readb(base + S3C2410_RTCCON);
373                         writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
374                 }
375 
376                 if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
377                         dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
378 
379                         tmp = readb(base + S3C2410_RTCCON);
380                         writeb(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 {
 95         unsigned int tmp;
 96 
 97         if (!is_power_of_2(freq))
 98                 return -EINVAL;
 99 
100         spin_lock_irq(&s3c_rtc_pie_lock);
101 
102         tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
103         tmp |= (128 / freq)-1;
104 
105         writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
106         spin_unlock_irq(&s3c_rtc_pie_lock);
107 
108         return 0;
109 }
首先检测freq这个值是否合法,然后读取TICNT寄存器,并将低7位清0,然后计算得tick count值,并将该值写入寄存器TICNT中。

在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 {
294         struct platform_device *pdev = to_platform_device(dev);
295         struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
296         int ret;
297 
298         ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
299                           IRQF_DISABLED,  "s3c2410-rtc alarm", rtc_dev);
300 
301         if (ret) {
302                 dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
303                 return ret;
304         }
305 
306         ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
307                           IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);
308 
309         if (ret) {
310                 dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
311                 goto tick_err;
312         }
313 
314         return ret;
315 
316  tick_err:
317         free_irq(s3c_rtc_alarmno, rtc_dev);
318         return ret;
319 }
在open函数中,主要申请了两个中断s3c_rtc_alarmirq和s3c_rtc_tickirq。而在release函数中,则是释放这两个申请:
321 static void s3c_rtc_release(struct device *dev)
322 {
323         struct platform_device *pdev = to_platform_device(dev);
324         struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
325 
326         /* do not clear AIE here, it may be needed for wake */
327 
328         s3c_rtc_setpie(dev, 0);
329         free_irq(s3c_rtc_alarmno, rtc_dev);
330         free_irq(s3c_rtc_tickno, rtc_dev);
331 }

再来看gettime操作:
113 static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
114 {
115         unsigned int have_retried = 0;
116         void __iomem *base = s3c_rtc_base;
117 
118  retry_get_time:
119         rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
120         rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
121         rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
122         rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);
123         rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
124         rtc_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 
131         if (rtc_tm->tm_sec == 0 && !have_retried) {
132                 have_retried = 1;
133                 goto retry_get_time;
134         }
135 
136         pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
137                  rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
138                  rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
139 
140         rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
141         rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
142         rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
143         rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
144         rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
145         rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
146 
147         rtc_tm->tm_year += 100;
148         rtc_tm->tm_mon -= 1;
149 
150         return 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 {
155         void __iomem *base = s3c_rtc_base;
156         int year = tm->tm_year - 100;
157 
158         pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
159                  tm->tm_year, tm->tm_mon, tm->tm_mday,
160                  tm->tm_hour, tm->tm_min, tm->tm_sec);
161 
162         /* we get around y2k by simply not supporting it */
163 
164         if (year < 0 || year >= 100) {
165                 dev_err(dev, "rtc only supports 100 years\n");
166                 return -EINVAL;
167         }
168 
169         writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);
170         writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);
171         writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
172         writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
173         writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
174         writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
175 
176         return 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 {
181         struct rtc_time *alm_tm = &alrm->time;
182         void __iomem *base = s3c_rtc_base;
183         unsigned int alm_en;
184 
185         alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
186         alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
187         alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
188         alm_tm->tm_mon  = readb(base + S3C2410_ALMMON);
189         alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
190         alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
191 
192         alm_en = readb(base + S3C2410_RTCALM);
193 
194         alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
195 
196         pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
197                  alm_en,
198                  alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
199                  alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
200 
201 
202         /* decode the alarm enable field */
203 
204         if (alm_en & S3C2410_RTCALM_SECEN)
205                 alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
206         else
207                 alm_tm->tm_sec = 0xff;
208 
209         if (alm_en & S3C2410_RTCALM_MINEN)
210                 alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
211         else
212                 alm_tm->tm_min = 0xff;
213 
214         if (alm_en & S3C2410_RTCALM_HOUREN)
215                 alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
216         else
217                 alm_tm->tm_hour = 0xff;
218 
219         if (alm_en & S3C2410_RTCALM_DAYEN)
220                 alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
221         else
222                 alm_tm->tm_mday = 0xff;
223 
224         if (alm_en & S3C2410_RTCALM_MONEN) {
225                 alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
226                 alm_tm->tm_mon -= 1;
227         } else {
228                 alm_tm->tm_mon = 0xff;
229         }
230 
231         if (alm_en & S3C2410_RTCALM_YEAREN)
232                 alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
233         else
234                 alm_tm->tm_year = 0xffff;
235 
236         return 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 {
241         struct rtc_time *tm = &alrm->time;
242         void __iomem *base = s3c_rtc_base;
243         unsigned int alrm_en;
244 
245         pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
246                  alrm->enabled,
247                  tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
248                  tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
249 
250 
251         alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
252         writeb(0x00, base + S3C2410_RTCALM);
253 
254         if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
255                 alrm_en |= S3C2410_RTCALM_SECEN;
256                 writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);
257         }
258 
259         if (tm->tm_min < 60 && tm->tm_min >= 0) {
260                 alrm_en |= S3C2410_RTCALM_MINEN;
261                 writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);
262         }
263 
264         if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
265                 alrm_en |= S3C2410_RTCALM_HOUREN;
266                 writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
267         }
268 
269         pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
270 
271         writeb(alrm_en, base + S3C2410_RTCALM);
272 
273         s3c_rtc_setaie(alrm->enabled);
274 
275         if (alrm->enabled)
276                 enable_irq_wake(s3c_rtc_alarmno);
277         else
278                 disable_irq_wake(s3c_rtc_alarmno);
279 
280         return 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 {
 63         unsigned int tmp;
 64 
 65         pr_debug("%s: aie=%d\n", __func__, to);
 66 
 67         tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 68 
 69         if (to)
 70                 tmp |= S3C2410_RTCALM_ALMEN;
 71 
 72         writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
 73 }
如果to是1,表示使能alarm,如果是0,表示禁止alarm。
最后根据alrm->enabled这个值来打开alarm的中断。至此,整个驱动程序也就分析完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值