一、 中斷系統硬件架構:
arm cortex-A9,A15上的中斷控制器為GIC400(低版本有GIC390,PL190等),硬件的邏輯圖為
EXTINT0-EXTINT2:分別設置EINT0—EINT7、EINT8—EINT15、EINT16—EINT23的觸發方式(高電平觸發、低電平觸發、下降沿觸發、上升沿觸發)。
EINTPEND:這個是中斷掛起寄存器,清除時要寫1,後面還有幾個是寫1清除。當一個外部中斷(EINT4-EINT23)發生後,那麼相應的位會被置1。為什麼沒有EINT0-EINT3,因為它們分別由SRCPND寄存器的後4位控制。
EINTMASK:這個簡單,是屏蔽中斷用的,也就是說位為1時,此次中斷無效。
內部中斷有8個寄存器:
SUBSRCPND:當一個中斷發生後,那麼相應的位會被置1,表示一個中斷發生了。
INTSUBMSK:與上一個是一伙的,中斷屏蔽寄存器。
SRCPND:當一個中斷發生後,那麼相應的位會被置1,表示一個或一類中斷發生了。
INTMSK:用來屏蔽SRCPND寄存器所標識的中斷。但只能屏蔽IRQ中斷,不能屏蔽FIQ中斷。
INTMOD:當INTMOD中某位被設置為1時,它對應的中斷被設為FIQ,CPU將進入快速中斷模式。
PRIORITY:用於設置IRQ中斷的優先級。
INTPND:中斷優先級仲裁器選出優先級最高中斷後,這個中斷在INTPND寄存器中的相應位被置1,隨後,CPU進入中斷模式處理它。同一時間內,此寄存器只有一位被置1。
INTOFFSET:用來表示INTPND寄存器中哪位被置1了,即記錄INTPND中位[x]為1的位x的值。清除INTPND、SRCPND時自動清除。
以上寄存器描述摘自論壇帖子,源作者不詳。
二、中斷系統軟件抽象架構
1)下圖為Linux內核中斷系統相關數據結構的關系圖, 在《深入linux內核架構》中將中斷系統分為三個層次:
High-level Interrupt Service routine:設備驅動的中斷處理程序,對應圖中的irqacton。
Interrupt Flow Handling:中斷流程控制(原書翻譯為中斷電流控制,我認為不准,雖然該部分和信號源狀態有關,例如邊沿觸發的中斷或者電平觸發的中斷,但它主要負責仍是:何時調用硬件操作的mask、ack等流程上的控制)。
Chip-Level hardware Encapsulation:硬件設備層,mask、ack等中斷系統硬件寄存器的操作,對應到圖中的irq_chip。
2)irq_desc:是一個全局數組,每個中斷源對應一個descriptor。
3)handle_irq:它為每個不同類型中斷源的處理函數入口,調用路徑為:asm_do_IRQ->handle_irq->generic_handle_irq->generic_handle_irq_desc-> handle_irq。不同類型中斷源使用不同的處理方式,在該中斷子系統初始化時使用函數irq_set_handler進行設置或直接給函數指針賦值。
irq_chip:它替代了老版本hw_interrupt_type,該結構體內的函數集為對應中斷源硬件操作的mask、unmask、應答ack、mask_ack等,在該中斷子系統初始化時使用函數irq_set_chip進行設置。
handle_level_irq處理電平信號觸發的中斷源類型,該函數會調用chip的irq_mask、irq_ack來mask並ack(告訴中斷控制器,已經有人響應該中斷了,可以繼續產生下一個新中斷了)這個中斷信號源,否則該中斷會被重復處理;
handle_egde_irq處理邊沿信號觸發的中斷源類型,只ack就可以,不需要mask,下一個新的該類型中斷可以pending到SRCPEND寄存器。
對於多核 CPU,如果其他核正在處理該中斷,可以通過函數irqd_irq_inprogress檢測 irq_desc ->irq_data->state_use_accessors狀態是否為IRQD_IRQ_INPROGRESS來識別。
handle_irq(=handle_level_irq or handle_egde_irq or ...)會通過handle_irq_event->handle_irq_event_percpu(2.6版本為handle_IRQ_event)調用真正的設備中斷處理函數action->handler。
4)irqaction:它對應具體設備的中斷描述,handler為具體isr中斷服務函數,dev_id用來區別共享中斷中的不同設備,next指針指向同一個中斷源(共享中斷)下一個設備的irqaction,當中斷發生,共享中斷的設備isr都將被調用,因此各個設備的isr內要有能力判斷該中斷是不是本設備發出的。關於共享中斷《Linux設備驅動》的一段話:“無論何時 2 個或多個驅動在共享中斷線, 並且硬件中斷在這條線上中斷處理器, 內核為這個中斷調用每個注冊的處理者, 傳遞它的 dev_id 給每個. 因此, 一個共享的處理者必須能夠識別它自己的中斷並且應當快速退出當它自己的設備沒有被中斷時.”我認為這個說法是錯誤的,首先中斷信號只是io上的一個電信號,不可能傳遞dev_id信息給cpu,那麼內核也不知道是哪個設備發出的中斷信號;再者如果真的有了已知的dev_id,也就不需要調用所有的共享中斷的isr了。
因此,要每個設備的isr來判斷該中斷是不是自己發出的,需要硬件支持,比如通過簡單的check一個設備的標志寄存器來完成,不是本設備的,立刻退出中斷。
另外,在處理共享中斷時不能單獨在某個設備內進行disable、enable irq的動作,這樣會影響其他共享中斷的設備。
三、中斷的軟件處理流程