歡迎轉載,轉載時需保留做者信息,謝謝。 html
郵箱:tangzhongp@163.com linux
博客園地址:http://www.cnblogs.com/embedded-tzp windows
Csdn博客地址:http://blog.csdn.net/xiayulewa 數據結構
環境: http://www.cnblogs.com/embedded-tzp/p/4443876.html dom
從頭介紹中斷原理太麻煩,通常來講看這篇文章的人對中斷必然已經有了必定的認識。無論哪款處理器,中斷的基本概念是同樣的:ide
中斷向量表。函數
中斷優先級。spa
中斷現場保護與恢復。.net
中斷向量表,程序發生中斷後會跳轉到該表執行,通常在裏面放置mov pc, addr_of_isr_func形式的指令,以中斷向量表爲跳板,跳轉到中斷服務程序中。指針
通常處理器中斷向量表地址是固定的,arm920t固定在0x00000000,或者0xFFFF0000處,其地址是經過配置CP15的Register 1實現的。詳細可閱讀ARM920T_TRM1_S.pdf
默認中斷向量表是在0x00000000處,arm920t的中斷向量表以下:
上圖爲arm中斷處理的流程,在程序正常運行時, 發生定時器中斷,跳轉到中斷向量表處執行對應指令,而irq中斷向量地址指令爲goto irq_isr, 跳轉到中斷irq中斷服務程序,此時首先保存現場,執行完中斷邏輯後,再恢復現場,後中斷返回,從以前被中斷處的下一條指令繼續運行。
.section .vectors, "ax", %progbits
__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, __vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
硬件:有主中斷SRCPND,子中斷SUBSRCPND。主中斷也可稱爲父中斷。
處理器中斷初始化: MACHINE_START:s3c2440_init_irq中初始化, 同時初始化static struct s3c_irq_intc *s3c_intc[3];變量
中斷(irq,abt,und)的堆棧初始化setup_arch→setup_processor→cpu_init→struct stack *stk = &stacks[cpu] (irq,abt,und)
映射中斷向量表:setup_arch→paging_init→devicemaps_init
定義中斷向量表:src\arch\arm\kernel\entry-armv.S(其大部分定義在entry-header.S (src\arch\arm\kernel)):__vectors_start
中斷方式初始化:如上升沿等,在init_s3c2440base裏面定義
假定程序運行在用戶態,中斷類型爲irq,則:
中斷髮生→W(b) vector_irq( vector_irq 由vector_stub irq, IRQ_MODE, 4定義)→執行宏vector_stub定義的vector_irq中斷服務程序, 定義見下:
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
vector_stub的做用是保存中斷現場,獲取中斷前處理器狀態,而後將狀態設置爲SVC mode,後跳轉到相應的中斷才處理程序。 前面假定中斷前爲用戶態,因此中斷髮生後,會最終跳轉到__irq_usr處執行。
__irq_usr→irq_handler→handle_arch_irq(是個函數指針,全局變量=s3c24xx_handle_irq, 在s3c24xx_init_intc中被賦值)→s3c24xx_handle_intc→handle_IRQ→generic_handle_irq→desc->handle_irq→若是是父中斷,則執行s3c_irq_demux(在s3c24xx_irq_map中設置,執行chained_irq_enter→irq_ack清除主中斷)→generic_handle_irq→desc->handle_irq即handle_edge_irq(chip->irq_ack清除次級中斷相關位,在s3c24xx_irq_map中設置)→handle_irq_event→ handle_irq_event_percpu→action->handler(由request_irq申請,循環處理action = action->next)
以__irq_usr爲例:b ret_to_user_from_irq(entry-common.S (src\arch\arm\kernel)定義)→ work_pending (在entry-common.S (src\arch\arm\kernel)定義)→ do_work_pending→ schedule
handle_arch_irq賦值流程:MACHINE_START的init_irq(s3c2440_init_irq)→s3c24xx_init_intc→set_handle_irq
desc->handle_irq註冊流程:MACHINE_START::s3c2440_init_irq→s3c24xx_init_intc→irq_domain_add_legacy(傳入
s3c24xx_irq_ops)→ops->map(即s3c24xx_irq_ops->map),即s3c24xx_irq_map→根據中斷類型若是是邊沿觸發,則
desc->handle_irq=handle_edge_irq
用到的變量與文件對應關係:
src\arch\arm\include\asm\Unified.h: #define PSR_ISETSTATE 0
src\arch\arm\include\asm\linkage.h: #define ENDPROC(name) \
.type name, %function; \
END(name)
src\include\linux\linkage.h:#define END(name) \
.size name, .-name
#endif
Asm-offsets.c (src\arch\arm\kernel): DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs))
entry-header.S (src\arch\arm\kernel)): why .req r8 @ Linux syscall (!= 0)
Unified.h (src\arch\arm\include\asm):#define BSYM(sym) sym
src\arch\arm\kernel\Asm-offsets.c: DEFINE(SYS_ERROR0, 0x9f0000);
request_irq→request_threaded_irq→__setup_irq→new->irq = irq; *old_ptr = new;(old_ptr指向action鏈表的最後一項)
linux中斷地址分配:MACHINE_START中mini2440_map_io→s3c24xx_init_io →iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); // 該函數分析詳細見gpio章節。
由上述可見,#define S3C_VA_IRQ S3C_ADDR(0x00000000), 知irq虛擬地址爲0xF6000000,且由於串口物理地址和虛擬地址對應關係肯定了,irq物理地址和虛擬地址也肯定了,由於PA1-PA2 = VA1-VA2
由上圖可見,中斷號能夠索引全部的中斷對象。
中斷概貌:最重要的結構是全局變量struct irq_desc irq_desc[NR_IRQS]; 每個中斷號對應一個struct irq_desc, 在MACHINE_START中的s3c2440_init_irq主要是初始化該結構體。每一箇中斷號和具體的irq_desc[i]對應,根據中斷號就能知道該中斷的全部信息,如該中斷是父中斷仍是子中斷,該中斷的處理函數。父中斷讀取子中斷源,經過desc->handle_irq = s3c_irq_demux(不必定是這個函數,爲了描述方便舉例的,後同)執行具體的子中斷函數,子中斷再執行desc->handle_irq = handle_edge_irq,最終都會
執行desc->action->handler。 總結起來 desc->handle_irq是執行函數分配,desc->action->handler執行具體的中斷函數,
1.4. 函數集合
request_irq
free_irq