我們先來看一下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。
- Jul 14 Tue 2015 14:47
ARM Linux对中断的处理
close
全站熱搜
留言列表
發表留言