天天看點

ARM Linux對中斷的處理--中斷管理系統的初始化

中斷管理系統的初始化

http://blog.chinaunix.net/uid-12567959-id-160975.html

我們先來看一下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   

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   

588

589   irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

590

591   for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {

592           

593

594           switch (irqno) {

595                   

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                   

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   

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   

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   

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       

689       desc->chip = &dummy_irq_chip;

690   }

691

692   chip_bus_lock(irq, desc);

693   raw_spin_lock_irqsave(&desc->lock, flags);

694

695   

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。

繼續閱讀