08-实例_S3C6410实时钟中断
10.3.4 实例:S3C6410实时钟中断
S3C6410处理器内部集成了实时钟(RTC)模块,该模块能够在系统断电的情况下由后备电池供电继续工作,其主要功能相对于一个时钟,记录年、月、日、时、分、秒等。S3C6410的RTC可产生两种中断:周期节拍(tick)中断和报警(alarm)中断,前者相当于一个周期性的定时器,后者相当于一个“闹钟”,它在预先设定的时间到来时产生中断。
S3C6410实时钟设备驱动(drivers/rtc/rtc-s3c.c,为S3C2410、S3C64XX、S5PC1XX、S5P64XX多种CPU共享)的open()函数中,会申请它将要使用的中断,如代码清单10.4所示。
代码清单10.4 S3C6410实时钟驱动open()函数
1 static int s3c_rtc_open(struct device *dev)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
5 int ret;
6 /申请alarm中断/
7 ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
8 IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
9
10 if (ret) {
11 dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
12 return ret;
13 }
14
15 /申请tick中断/
16 ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
17 IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
18
19 if (ret) {
20 dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
21 goto tick_err;
22 }
23
24 return ret;
25
26 tick_err:
27 free_irq(s3c_rtc_alarmno, rtc_dev);
28 return ret;
29 }
S3C6410实时钟设备驱动的release()函数中,会释放它将要使用的中断,如代码清单10.5所示。
代码清单10.5 S3C6410实时钟驱动release()函数
1 static void s3c_rtc_release(struct device *dev)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
5
6 s3c_rtc_setpie(dev, 0);
7 /释放中断/
8 free_irq(s3c_rtc_alarmno, rtc_dev);
9 free_irq(s3c_rtc_tickno, rtc_dev);
10 }
S3C6410实时钟驱动的中断处理比较简单,没有明确地分为上下两个半部,而只存在顶半部,如代码清单10.6所示。
代码清单10.6 S3C6410实时钟驱动中断处理程序
1 static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
2 {
3 struct rtc_device *rdev = id;
4
5 rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
6
7 s3c_rtc_set_bit_byte(s3c_rtc_base,S3C2410_INTP,S3C2410_INTP_ALM);
8
9 return IRQ_HANDLED;
10 }
11
12 static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
13 {
14 struct rtc_device *rdev = id;
15
16 rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
17
18 s3c_rtc_set_bit_byte(s3c_rtc_base,S3C2410_INTP,S3C2410_INTP_TIC);
19
20 return IRQ_HANDLED;
21 }
上述代码中调用的rtc_update_irq()函数定义于drivers/rtc/interface.c文件中,被各种实时钟驱动共享,如代码清单10.7所示。
代码清单10.7 实时钟更新rtc_update_irq()函数
1 void rtc_update_irq(struct rtc_device *rtc,
2 unsigned long num, unsigned long events)
3 {
4 spin_lock(&rtc->irq_lock);
5 rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
6 spin_unlock(&rtc->irq_lock);
7
8 spin_lock(&rtc->irq_task_lock);
9 if (rtc->irq_task)
10 rtc->irq_task->func(rtc->irq_task->private_data);
11 spin_unlock(&rtc->irq_task_lock);
12
13 wake_up_interruptible(&rtc->irq_queue);
14 kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
15 }
上述中断处理程序并没有底半部(或者说没有严格意义上的tasklet、工作队列或软中断底半部),实际上,它只是唤醒一个等待队列rtc->irq_queue并发出一个SIGIO信号,而这个等待队列的唤醒也将导致一个阻塞的进程被执行(这个阻塞的进程可看作底半部)。现在我们看到,等待队列可以作为中断处理程序顶半部和进程同步的一种良好机制。但是,任何情况下,都不能在顶半部等待一个等待队列,而只能唤醒。