ARM Linux对中断的处理--中断管理系统的初始化

中断管理系统的初始化
中断管理系统的初始化
我们先来看一下Linux系统中,中断管理系统的初始化。中断系统的初始化主要由几个函数来完成。在系统初始化的start_kernel()函数 (在文件init/main.c中定义)中可以看到:
asmlinkage void __init start_kernel(void)
{
……
   trap_init();
……
   early_irq_init();
   init_IRQ();
……
}
start_kernel()函数调用trap_init()、early_irq_init()和init_IRQ()三个函数来初始化中断管理系统。对于我们的ARM平台来说trap_init()在arch/arm/kernel/traps.c中定义,为一个空函数。
 
early_irq_init()函数
在start_kernel()函数中调用了early_irq_init()函数,这个函数在kernel/handle.c文件中定义。kernel/handle.c文件中,根据内核配置时是否选择了CONFIG_SPARSE_IRQ,而可以选择两个不同版本的该函数early_irq_init()中的一个进行编译。CONFIG_SPARSE_IRQ配置项,用于支持稀疏irq号,对于发行版的内核很有用,它允许定义一个高CONFIG_NR_CPUS值,但仍然不希望消耗太多内存的情况。对于我们的开发板来说,自然不需要打开这个配置项。
 
early_irq_init()函数定义如下:
int __init early_irq_init(void)
{
   struct irq_desc *desc;
   int count;
   int i;
 
   init_irq_default_affinity();
 
   printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
 
   desc = irq_desc;
   count = ARRAY_SIZE(irq_desc);
 
   for (i = 0; i < count; i++) {
      desc[i].irq = i;
      alloc_desc_masks(&desc[i], 0, true);
      init_desc_masks(&desc[i]);
      desc[i].kstat_irqs = kstat_irqs_all[i];
   }
   return arch_early_irq_init();
}
主要工作即为初始化用于管理中断的irq_desc[NR_IRQS]数组的每个元素,它主要设置数组中每一个成员的中断号,使得数组中每一个元素的kstat_irqs字段(irq stats per cpu),指向定义的二维数组中的对应的行。alloc_desc_masks(&desc[i], 0, true)和init_desc_masks(&desc[i])函数在非SMP平台上为空函数。arch_early_irq_init()在主要用于x86平台和PPC平台,其他平台上为空函数。
 
 
init_IRQ()函数
init_IRQ(void)函数是一个特定于体系结构的函数,对于ARM体系结构来说该函数定义如下:
---------------------------------------------------------------------
arch/arm/kernel/irq.c
152 void __init init_IRQ(void)
153 {
154     int irq;
155
156     for (irq = 0; irq < NR_IRQS; irq++)
157            irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
158
159     init_arch_irq();
160 }
---------------------------------------------------------------------
我们看到这个函数将irq_desc[NR_IRQS]结构数组各个元素的状态字段设置为IRQ_NOREQUEST | IRQ_NOPROBE,也就是未请求和未探测状态。然后调用特定机器平台的中断初始化init_arch_irq()函数。而init_arch_irq()实际上是一个函数指针,其定义如下:
---------------------------------------------------------------------
arch/arm/kernel/irq.c
50 void (*init_arch_irq)(void) __initdata = NULL;
---------------------------------------------------------------------
 
在前面的博文ARM linux的启动部分源代码简略分析 中我们已经说明了setup_arch()函数是如何获取machine_desc,而又如何初始化init_arch_irq函数指针变量的。我们知道,对于我们的mini2440开发板,这个函数指针指向的函数为s3c24xx_init_irq()函数,该函数定义如下在arch/arm/plat-s3c24xx/irq.c中定义:
---------------------------------------------------------------------
arch/arm/plat-s3c24xx/irq.c
535 void __init s3c24xx_init_irq(void)
536 {
537   unsigned long pend;
538   unsigned long last;
539   int irqno;
540   int i;
541
542 #ifdef CONFIG_FIQ
543   init_FIQ();
544 #endif
545
546   irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");
547
548   /* first, clear all interrupts pending... */
549
550   last = 0;
551   for (i = 0; i < 4; i++) {
552           pend = __raw_readl(S3C24XX_EINTPEND);
553
554           if (pend == 0 || pend == last)
555                   break;
556
557           __raw_writel(pend, S3C24XX_EINTPEND);
558       printk("irq: clearing pending ext status %08x\n", (int)pend);
559           last = pend;
560   }
561
562   last = 0;
563   for (i = 0; i < 4; i++) {
564           pend = __raw_readl(S3C2410_INTPND);
565
566           if (pend == 0 || pend == last)
567                    break;
568
569           __raw_writel(pend, S3C2410_SRCPND);
570           __raw_writel(pend, S3C2410_INTPND);
571           printk("irq: clearing pending status %08x\n", (int)pend);
572           last = pend;
573   }
574
575   last = 0;
576   for (i = 0; i < 4; i++) {
577           pend = __raw_readl(S3C2410_SUBSRCPND);
578
579           if (pend == 0 || pend == last)
580                   break;
581
582        printk("irq: clearing subpending status %08x\n", (int)pend);
583           __raw_writel(pend, S3C2410_SUBSRCPND);
584           last = pend;
585   }
586
587   /* register the main interrupts */
588
589   irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");
590
591   for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
592           /* set all the s3c2410 internal irqs */
593
594           switch (irqno) {
595                   /* deal with the special IRQs (cascaded) */
596
597           case IRQ_EINT4t7:
598           case IRQ_EINT8t23:
599           case IRQ_UART0:
600           case IRQ_UART1:
601           case IRQ_UART2:
602           case IRQ_ADCPARENT:
603                   set_irq_chip(irqno, &s3c_irq_level_chip);
604                   set_irq_handler(irqno, handle_level_irq);
605                   break;
606
607           case IRQ_RESERVED6:
608           case IRQ_RESERVED24:
609                   /* no IRQ here */
610                   break;
611
612           default:
613                  //irqdbf("registering irq %d (s3c irq)\n", irqno);
614                   set_irq_chip(irqno, &s3c_irq_chip);
615                   set_irq_handler(irqno, handle_edge_irq);
616                   set_irq_flags(irqno, IRQF_VALID);
617           }
618   }
619
620   /* setup the cascade irq handlers */
621
622   set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
623   set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
624
625   set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
626   set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
627   set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
628   set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
629
630   /* external interrupts */
631
632   for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
633           irqdbf("registering irq %d (ext int)\n", irqno);
634           set_irq_chip(irqno, &s3c_irq_eint0t4);
635           set_irq_handler(irqno, handle_edge_irq);
636           set_irq_flags(irqno, IRQF_VALID);
637   }
638
639   for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
640           irqdbf("registering irq %d (extended s3c irq)\n", irqno);
641           set_irq_chip(irqno, &s3c_irqext_chip);
642           set_irq_handler(irqno, handle_edge_irq);
643           set_irq_flags(irqno, IRQF_VALID);
644   }
645
646   /* register the uart interrupts */
647
648   irqdbf("s3c2410: registering external interrupts\n");
649
650   for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
651           irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
652           set_irq_chip(irqno, &s3c_irq_uart0);
653           set_irq_handler(irqno, handle_level_irq);
654           set_irq_flags(irqno, IRQF_VALID);
655   }
656
657   for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
658           irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
659           set_irq_chip(irqno, &s3c_irq_uart1);
660           set_irq_handler(irqno, handle_level_irq);
661           set_irq_flags(irqno, IRQF_VALID);
662   }
663
664 for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++){
665           irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
666           set_irq_chip(irqno, &s3c_irq_uart2);
667           set_irq_handler(irqno, handle_level_irq);
668           set_irq_flags(irqno, IRQF_VALID);
669   }
670
671   for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
672           irqdbf("registering irq %d (s3c adc irq)\n", irqno);
673           set_irq_chip(irqno, &s3c_irq_adc);
674           set_irq_handler(irqno, handle_edge_irq);
675           set_irq_flags(irqno, IRQF_VALID);
676   }
677
678   irqdbf("s3c2410: registered interrupt handlers\n");
679 }
---------------------------------------------------------------------
话说这个函数看上去挺长的,但好像没有什么难以理解的地方,它完成对的主要工作如下:
1、清除外部中断的中断挂起位。读取中断挂起寄存器的内容,并将该内容重新写入中断挂起寄存器(将相应位写入1,可以清除相应的位,更详细的情况可以参考手册)
2、清除中断的中断挂起位。
3、清除子中断源的中断挂起位。
4、进一步的完成irq_desc[NR_IRQS]结构数组的初始化,主要为设置元素的chip字段和handle_irq字段(高层中断处理程序)和标志字段。设置的内容,则因中断类型的不同而不同。
 
s3c24xx_init_irq()函数调用的用于设置irq_desc结构体的许多相关的函数也是挺有意思的。先看set_irq_chip()函数,在文件kernel/irq/chip.c中定义:
---------------------------------------------------------------------
kernel/irq/chip.c
128 int set_irq_chip(unsigned int irq, struct irq_chip *chip)
129 {
130   struct irq_desc *desc = irq_to_desc(irq);
131   unsigned long flags;
132
133   if (!desc) {
134        WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
135        return -EINVAL;
136   }
137
138   if (!chip)
139        chip = &no_irq_chip;
140
141   raw_spin_lock_irqsave(&desc->lock, flags);
142   irq_chip_set_defaults(chip);
143   desc->chip = chip;
144   raw_spin_unlock_irqrestore(&desc->lock, flags);
145
146   return 0;
147 }
148 EXPORT_SYMBOL(set_irq_chip);
---------------------------------------------------------------------
这个函数为一个irq设置irq chip。它接收两个参数,irq为中断号,chip则为指向irq chip描述结构的指针。这个函数会首先调用irq_to_desc(irq)来获得相应中断号的中断描述符的指针,然后调用irq_chip_set_defaults(chip)来对做进一步的完善,即对于某些不能为空的成员,则使其指向默认的处理函数。最后设置中断描述符的chip字段为传进来的chip参数,以在中断发生或者管理中断时可以完成对于PIC的操作。
 
对于IRQ_EINT4t7、IRQ_EINT8t23、IRQ_UART0、IRQ_UART1、IRQ_UART2和IRQ_ADCPARENT等有多个子中断源的中断,其chip设置为s3c_irq_level_chip,其定义如下:
---------------------------------------------------------------------
arch/arm/plat-s3c24xx/irq.c
86 struct irq_chip s3c_irq_level_chip = {
 87         .name           = "s3c-level",
 88         .ack            = s3c_irq_maskack,
 89         .mask           = s3c_irq_mask,
 90         .unmask         = s3c_irq_unmask,
 91         .set_wake       = s3c_irq_wake
 92 };
---------------------------------------------------------------------
对于其他中断线,这其chip被设为s3c_irq_chip:
---------------------------------------------------------------------
arch/arm/plat-s3c24xx/irq.c
94 struct irq_chip s3c_irq_chip = {
 95         .name           = "s3c",
 96         .ack            = s3c_irq_ack,
 97         .mask           = s3c_irq_mask,
 98         .unmask         = s3c_irq_unmask,
 99         .set_wake       = s3c_irq_wake
100 };
---------------------------------------------------------------------
 
接着看set_irq_handler()函数,其定义如下:
---------------------------------------------------------------------
include/linux/irq.h
367 set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
368 {
369         __set_irq_handler(irq, handle, 0, NULL);
370 }
---------------------------------------------------------------------
这个函数用于设置给定的IRQ的高层处理程序。它仅仅是对于__set_irq_handler()函数的一个封装。__set_irq_handler()函数定义如下:
---------------------------------------------------------------------
include/linux/irq.h
664 void
665 __set_irq_handler(unsigned int irq, irq_flow_handler_t handle,
666                  int is_chained, const char *name)
667 {
668   struct irq_desc *desc = irq_to_desc(irq);
669   unsigned long flags;
670
671   if (!desc) {
672       printk(KERN_ERR
673              "Trying to install type control for IRQ%d\n", irq);
674       return;
675   }
676
677   if (!handle)
678       handle = handle_bad_irq;
679   else if (desc->chip == &no_irq_chip) {
680       printk(KERN_WARNING "Trying to install %sinterrupt handler "
681              "for IRQ%d\n", is_chained ? "chained " : "", irq);
682       /*
683        * Some ARM implementations install a handler for really dumb
684        * interrupt hardware without setting an irq_chip. This worked
685        * with the ARM no_irq_chip but the check in setup_irq would
686        * prevent us to setup the interrupt at all. Switch it to
687        * dummy_irq_chip for easy transition.
688        */
689       desc->chip = &dummy_irq_chip;
690   }
691
692   chip_bus_lock(irq, desc);
693   raw_spin_lock_irqsave(&desc->lock, flags);
694
695   /* Uninstall? */
696   if (handle == handle_bad_irq) {
697           if (desc->chip != &no_irq_chip)
698                   mask_ack_irq(desc, irq);
699           desc->status |= IRQ_DISABLED;
700           desc->depth = 1;
701   }
702   desc->handle_irq = handle;
703   desc->name = name;
704
705   if (handle != handle_bad_irq && is_chained) {
706           desc->status &= ~IRQ_DISABLED;
707           desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
708           desc->depth = 0;
709           desc->chip->startup(irq);
710   }
711   raw_spin_unlock_irqrestore(&desc->lock, flags);
712   chip_bus_sync_unlock(irq, desc);
713 }
714 EXPORT_SYMBOL_GPL(__set_irq_handler);
---------------------------------------------------------------------
这个函数就是为特定的中断线设置好一个高层的中断处理例程,这里的处理例程可不是我们调用request_irq()时注册的中断处理例程。这个函数完成如下工作:
1、调用irq_to_desc(irq)获得相应中断号对应的irq_desc结构体的指针。
2、检查传递进来的handle参数,若为空,则设为handle_bad_irq;检查中断描述符的chip字段,若未设置,则设为dummy_irq_chip。
3、获取自旋锁,并在局部变量flags中保存标志。
4、检查handle,若为handle_bad_irq,即未安装,则屏蔽该中断,设置中断描述符状态字段的IRQ_DISABLED位,并设置中断描述符的depth为1。
5、设置中断描述符的handle_irq为handle,并设置中断描述符的name字段为传递进来的name参数。
6、如果传递进来的handle为有效值,同时传递进来的is_chained非零,则清除中断描述符status字段的IRQ_DISABLED,并设置IRQ_NOREQUEST、IRQ_NOPROBE位。设置中断描述符的depth为0。并对中断号调用desc->chip->startup(irq)。
7、释放自旋锁并恢复保存的标志flags。
 
接着看set_irq_flags()函数,其定义如下:
---------------------------------------------------------------------
arch/arm/kernel/irq.c
130 void set_irq_flags(unsigned int irq, unsigned int iflags)
131 {
132         struct irq_desc *desc;
133         unsigned long flags;
134
135         if (irq >= NR_IRQS) {
136                 printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
137                 return;
138         }
139
140         desc = irq_desc + irq;
141         raw_spin_lock_irqsave(&desc->lock, flags);
142         desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
143         if (iflags & IRQF_VALID)
144                 desc->status &= ~IRQ_NOREQUEST;
145         if (iflags & IRQF_PROBE)
146                 desc->status &= ~IRQ_NOPROBE;
147         if (!(iflags & IRQF_NOAUTOEN))
148                 desc->status &= ~IRQ_NOAUTOEN;
149         raw_spin_unlock_irqrestore(&desc->lock, flags);
150 }
---------------------------------------------------------------------
该函数主要是为特定的中断号对应的中断描述符设置相应的状态标记, 而s3c24xx_init_irq()函数里我们调用它的主要目的就是清掉IRQ_NOREQUEST标记,告诉系,该中断已经可以被申请使用了,中断在申请的时候会查看是否有IRQ_NOREQUEST标记,如有则表面该中断还不能使用。而初始化的时候所有的中断都有这个标记。
 
最后来看set_irq_chained_handler,其定义如下:
---------------------------------------------------------------------
include/linux/irq.h
377 static inline void
378 set_irq_chained_handler(unsigned int irq,
379                         irq_flow_handler_t handle)
380 {
381         __set_irq_handler(irq, handle, 1, NULL);
382 }
---------------------------------------------------------------------
这个函数同样也是__set_irq_handler()函数的封装,所不同的是,这个封装在调用__set_irq_handler()函数时第三个参数is_chained传递的是1,而不是0。在前面我们看到,其差别就是set_irq_chained_handler()在handle参数有效时,会直接清除中断描述符的status字段的IRQ_DISABLED位,以表示相应的中断线可用。
 

在s3c24xx_init_irq()函数中,我们看到只有为几个中断线设置高层中断处理程序时使用的是set_irq_chained_handler()以使相应的中断线直接可用,这几个中断线分别是 IRQ_EINT4t7、IRQ_EINT8t23、IRQ_UART0、IRQ_UART1、IRQ_UART2和IRQ_ADCPARENT,这与中断控制器有关,这几个中断线的每一个都关联着好几个子中断源。另外,也只有为这几个中断线设置时传递的handle参数为比较独特,而其他的则均为handle_edge_irq。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值