ARM+Linux中斷系統(tǒng)詳細(xì)分析
Linux中斷系統(tǒng)到底是否支持優(yōu)先級(jí),可否嵌套,中斷號(hào)又是怎么來(lái)確定的,中斷產(chǎn)生時(shí)又是如何一步步執(zhí)行到中斷處理函數(shù)的。為了徹底搞懂Linux中斷系統(tǒng),我決定從最原始材料出發(fā),一探究竟。(s3c2440+linux2.6.21)
本文引用地址:http://butianyuan.cn/article/201611/317905.htm先來(lái)看看ARM的硬件執(zhí)行流程
異常是ARM處理器模式分類,ARM有七種運(yùn)行模式USR,SYS,SVC,IRQ,FIQ,UND,ABT
五種異常模式:SVC,IRQ,FIQ,UND,ABT
中斷模式是ARM異常模式之一(IRQ模式,F(xiàn)IQ模式),是一種異步事件,如外部按鍵產(chǎn)生中斷,內(nèi)部定時(shí)器產(chǎn)生中斷,通信數(shù)據(jù)口數(shù)據(jù)收發(fā)產(chǎn)生中斷等。
1.當(dāng)一個(gè)異常產(chǎn)生時(shí),以FIQ為例,CPU切入FIQ模式時(shí),
①將原來(lái)執(zhí)行程序的下一條指令地址保存到LR中,就是將R14保存到R14_fiq里面。
②拷貝CPSR到SPSR_fiq。
③改變CPSR模式位的值,改到FIQ模式。
④改變PC值,將其指向相應(yīng)的異常處理向量表。
離開(kāi)異常處理的時(shí)候,
①將LR(R14_fiq)賦給PC。
②將SPSR(SPSR_fiq)拷貝到CPSR。
③清除中斷禁止標(biāo)志(如果開(kāi)始時(shí)置位了)。
ARM一般在某個(gè)固定地址中有一個(gè)異常向量表,比如0x0
當(dāng)一個(gè)外部IRQ中斷產(chǎn)生時(shí)
①處理器切換到IRQ模式
②PC跳到0x18處運(yùn)行,因?yàn)檫@是IRQ的中斷入口。
③通過(guò)0x18:LDR PC, IRQ_ADDR,跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)程序。這個(gè)中斷服務(wù)程序就要確定中斷源,每個(gè)中斷源會(huì)有自己獨(dú)立的中斷服務(wù)程序。
④得到中斷源,然后執(zhí)行相應(yīng)中斷服務(wù)程序
⑤清除中斷標(biāo)志,返回
這就是一個(gè)外部中斷完整的執(zhí)行流程了,下面以具體寄存器來(lái)更具體的了解ARM的中斷機(jī)制。
假設(shè)ARM核有兩個(gè)中斷引腳,一根是irq pin,一根是fiq pin,正常情況下,ARM核只是機(jī)械地隨著PC指示去執(zhí)行,當(dāng)CPSR中的I位和F位都為1時(shí),IRQ和FIQ都處于禁止?fàn)顟B(tài),這時(shí)候無(wú)論發(fā)什么信號(hào),ARM都不會(huì)理睬。
當(dāng)I位或F位為0時(shí),irq pin有中斷信號(hào)過(guò)來(lái)時(shí),ARM當(dāng)前工作就會(huì)被打斷,切換到IRQ模式,并且跳轉(zhuǎn)到異常向量表的中斷入口0x18,SRCPND中相應(yīng)位置1,經(jīng)過(guò)檢查中斷優(yōu)先級(jí)寄存器以及屏蔽寄存器,確定中斷源,INTPND相應(yīng)位置1(經(jīng)過(guò)仲裁,只有一位置1),這過(guò)程由ARM自動(dòng)完成。0x18存放的是總的中斷處理函數(shù),在這個(gè)函數(shù)里,可以建立一個(gè)二級(jí)中斷向量表,先清除SRCPND相應(yīng)位,然后根據(jù)中斷源執(zhí)行相應(yīng)中斷服務(wù)程序,清除INTPND,返回。
及時(shí)清除中斷Pending寄存器的標(biāo)志位是為了避免兩個(gè)問(wèn)題:①發(fā)生中斷返回后,立即又被中斷,不斷的重復(fù)響應(yīng)②丟失中斷處理過(guò)程中發(fā)生的中斷,返回后不響應(yīng)。
在某個(gè)IRQ中斷程序執(zhí)行過(guò)程中,有另外一個(gè)外部IRQ中斷產(chǎn)生,會(huì)將SRCPND相應(yīng)位置1,等該中斷服務(wù)執(zhí)行完,經(jīng)過(guò)仲裁決定下一個(gè)要響應(yīng)的中斷。但是假如當(dāng)產(chǎn)生的是FIQ,則保存當(dāng)前IRQ的現(xiàn)場(chǎng),嵌套響應(yīng)FIQ,F(xiàn)IQ服務(wù)程序執(zhí)行完,再繼續(xù)執(zhí)行IRQ服務(wù)。那么當(dāng)一個(gè)FIQ正在服務(wù),產(chǎn)生另外一個(gè)FIQ,會(huì)怎樣呢,答案是不會(huì)被打斷,跟IRQ一樣等當(dāng)前中斷服務(wù)完成,再仲裁剩余需要相應(yīng)的中斷。
所以得出這樣的結(jié)論:
①關(guān)于中斷嵌套:IRQ模式只能被FIQ模式打斷,F(xiàn)IQ模式下誰(shuí)也打不斷。
②關(guān)于優(yōu)先級(jí):ARM核對(duì)中斷優(yōu)先級(jí),有明確的可編程管理。
下面再來(lái)看看Linux對(duì)ARM是怎么處理的,記住一個(gè)前提:Linux對(duì)ARM的硬件特性可以取舍,但不可更改。
1.建立異常向量表:
系統(tǒng)從arch/arm/kernel/head.S的ENTRY(stext)開(kāi)始執(zhí)行,__lookup_processor_type檢查處理器ID,__lookup_machine_type檢查機(jī)器ID,__create_page_tables創(chuàng)建頁(yè)表,啟動(dòng)MMU,然后由arch/arm/kernel/head_common.S跳到start_kernel()->trap_init()
點(diǎn)擊(此處)折疊或打開(kāi)
- void __init trap_init(void)
- {
- unsigned long vectors=CONFIG_VECTORS_BASE;
- …
- memcpy((void*)vectors,__vectors_start,__vectors_end-__vectors_start);
- memcpy((void*)vectors+0x200,__stubs_start,__stubs_end-__stubs_start);
- memcpy((void*)vectors+0x1000-kuser_sz,__kuser_helper_start,kuser_sz);
- …
- }
- #define CONFIG_VECTORS_BASE 0xffff0000
CONFIG_VECTORS_BASE在autoconf.h定義,在ARMV4及V4T以后的大部分處理器中,中斷向量表的位置可以有兩個(gè)位置:一個(gè)是0,另一個(gè)是0xffff0000??梢酝ㄟ^(guò)CP15協(xié)處理器c1寄存器中V位(bit[13])控制。V和中斷向量表的對(duì)應(yīng)關(guān)系如下:
V=0~0x00000000~0x0000001C
V=1~0xffff0000~0xffff001C
__vectors_end至__vectors_start之間為異常向量表,位于arch/arm/kernel/entry-armv.S
點(diǎn)擊(此處)折疊或打開(kāi)
- .globl __vectors_start
- __vectors_start:
- swi SYS_ERROR0
- b vector_und + stubs_offset//復(fù)位異常
- ldr pc, .LCvswi + stubs_offset //未定義異常
- b vector_pabt + stubs_offset//軟件中斷異常
- b vector_dabt + stubs_offset//數(shù)據(jù)異常
- b vector_addrexcptn + stubs_offset//保留
- b vector_irq + stubs_offset //普通中斷異常
- b vector_fiq + stubs_offset//快速中斷異常
- .globl __vectors_end
- __vectors_end:
stubs_offset值如下:
.equstubs_offset,__vectors_start+0x200-__stubs_start
stubs_offset是如何確定的呢?(引用網(wǎng)絡(luò)上的一段比較詳細(xì)的解釋)
當(dāng)匯編器看到B指令后會(huì)把要跳轉(zhuǎn)的標(biāo)簽轉(zhuǎn)化為相對(duì)于當(dāng)前PC的偏移量(±32M)寫(xiě)入指令碼。從上面的代碼可以看到中斷向量表和stubs都發(fā)生了代碼搬移,所以如果中斷向量表中仍然寫(xiě)成bvector_irq,那么實(shí)際執(zhí)行的時(shí)候就無(wú)法跳轉(zhuǎn)到搬移后的vector_irq處,因?yàn)橹噶畲a里寫(xiě)的是原來(lái)的偏移量,所以需要把指令碼中的偏移量寫(xiě)成搬移后的。我們把搬移前的中斷向量表中的irq入口地址記irq_PC,它在中斷向量表的偏移量就是irq_PC-vectors_start,vector_irq在stubs中的偏移量是vector_irq-stubs_start,這兩個(gè)偏移量在搬移前后是不變的。搬移后vectors_start在0xffff0000處,而stubs_start在0xffff0200處,所以搬移后的vector_irq相對(duì)于中斷向量中的中斷入口地址的偏移量就是,200+vector_irq在stubs中的偏移量再減去中斷入口在向量表中的偏移量,即200+vector_irq-stubs_start-irq_PC+vectors_start=(vector_irq-irq_PC)+vectors_start+200-stubs_start,對(duì)于括號(hào)內(nèi)的值實(shí)際上就是中斷向量表中寫(xiě)的vector_irq,減去irq_PC是由匯編器完成的,而后面的vectors_start+200-stubs_start就應(yīng)該是stubs_offset,實(shí)際上在entry-armv.S中也是這樣定義的。
2.中斷響應(yīng)
當(dāng)有外部中斷產(chǎn)生時(shí),跳轉(zhuǎn)到異常向量表的“bvector_irq + stubs_offset //普通中斷異常”
進(jìn)入異常處理函數(shù),跳轉(zhuǎn)的入口位置archarmkernelentry-armv.S代碼簡(jiǎn)略如下
點(diǎn)擊(此處)折疊或打開(kāi)
- .globl __stubs_start
- __stubs_start:
- /*
- * Interrupt dispatcher
- */
- 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)
- vector_stub dabt, ABT_MODE, 8
- vector_stub pabt, ABT_MODE, 4
- vector_stub und, UND_MODE
- /*
- * Undefined FIQs
- */
- vector_fiq:
- disable_fiq
- subs pc, lr, #4
- vector_addrexcptn:
- b vector_addrexcptn
vector_stub是個(gè)函數(shù)調(diào)用宏,根據(jù)中斷前的工作模式?jīng)Q定進(jìn)入__irq_usr,__irq_svc。這里入__irq_svc,同時(shí)看到這里FIQ產(chǎn)生時(shí),系統(tǒng)未做任何處理,直接返回,即Linux沒(méi)有提供對(duì)FIQ的支持,繼續(xù)跟進(jìn)代碼
點(diǎn)擊(此處)折疊或打開(kāi)
- __irq_svc:
- svc_entry
- …
- irq_handler
svc_entry是一個(gè)宏,主要實(shí)現(xiàn)了將SVC模式下的寄存器、中斷返回地址保存到堆棧中。然后進(jìn)入最核心的中斷響應(yīng)函數(shù)irq_handler,irq_handler實(shí)現(xiàn)過(guò)程archarmkernelentry-armv.S
點(diǎn)擊(此處)折疊或打開(kāi)
- .macro irq_handler
- get_irqnr_preamble r5, lr
- 1: get_irqnr_and_base r0, r6, r5, lr @判斷中斷號(hào),通過(guò)R0返回,3.5節(jié)有實(shí)現(xiàn)過(guò)程
- movne r1, sp
- @
- @ routine called with r0 = irq number, r1 = struct pt_regs *
- @
- adrne lr, 1b
- bne asm_do_IRQ @進(jìn)入中斷處理。
- ……
- .endm
get_irqnr_and_base中斷號(hào)判斷過(guò)程,include/asm/arch-s3c2410/entry-macro.s
點(diǎn)擊(此處)折疊或打開(kāi)
- .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
- mov base, #S3C24XX_VA_IRQ
- @@ try the interrupt offset register, since it is there
- ldr irqstat, [ base, #INTPND ]
- teq irqstat, #0
- beq 1002f
- ldr irqnr, [ base, #INTOFFSET ] @通過(guò)判斷INTOFFSET寄存器得到中斷位置
- …
- @@ we have the value
- 1001:
- adds irqnr, irqnr, #IRQ_EINT0 @加上中斷號(hào)的基準(zhǔn)數(shù)值,得到最終的中斷號(hào),注意:此時(shí)沒(méi)有考慮子中斷的具體情況。IRQ_EINT0在include/asm/arch-s3c2410/irqs.h中定義.從這里可以看出,中斷號(hào)的具體值是有平臺(tái)相關(guān)的代碼決定的,和硬件中斷掛起寄存器中的中斷號(hào)是不等的。
- 1002:
- @@ exit here, Z flag unset if IRQ
- .endm
asm_do_IRQ實(shí)現(xiàn)過(guò)程,arch/arm/kernel/irq.c
點(diǎn)擊(此處)折疊或打開(kāi)
- asmlinkage void asm_do_IRQ(unsignedintirq,struct pt_regs*regs)
- {
- struct pt_regs*old_regs=set_irq_regs(regs);
- struct irq_desc*desc=irq_desc+irq;//根據(jù)中斷號(hào),找到響應(yīng)的irq_desc
- /*
- *Some hardware gives randomly wrong interrupts.Rather
- *than crashing,dosomething sensible.
- */
- if(irq>=NR_IRQS)
- desc=&bad_irq_desc;
- irq_enter();
- desc_handle_irq(irq,desc);//根據(jù)irq和desc進(jìn)入中斷處理
- /*AT91 specific workaround*/
- irq_finish(irq);
- irq_exit();
- set_irq_regs(old_regs);
- }
- static inline void desc_handle_irq(unsignedintirq,struct irq_desc*desc)
- {
- desc->handle_irq(irq,desc);//中斷處理
- }
上述asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)使用了asmlinkage標(biāo)識(shí)。那么這個(gè)標(biāo)識(shí)的含義如何理解呢?
該符號(hào)定義在kernel/include/linux/linkage.h中,如下所示:
點(diǎn)擊(此處)折疊或打開(kāi)
- #ifdef __cplusplus
- #define CPP_ASMLINKAGE extern"C"
- #else
- #define CPP_ASMLINKAGE
- #endif
- #ifndef asmlinkage//如果以前沒(méi)有定義asmlinkage
- #define asmlinkage CPP_ASMLINKAGE
- #endif
對(duì)于ARM處理器的,沒(méi)有定義asmlinkage,所以沒(méi)有意義(不要以為參數(shù)是從堆棧傳遞的,對(duì)于ARM平臺(tái)來(lái)說(shuō)還是符合ATPCS過(guò)程調(diào)用標(biāo)準(zhǔn),通過(guò)寄存器傳遞的)。
但對(duì)于X86處理器的中是這樣定義的:
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
表示函數(shù)的參數(shù)傳遞是通過(guò)堆棧完成的。
中斷處理過(guò)程代碼就跟到這了,那么最后一個(gè)問(wèn)題desc->handle_irq(irq, desc);是怎么跟我們注冊(cè)的中斷函數(shù)相關(guān)聯(lián)的呢?再?gòu)闹袛嗄P妥?cè)入手:
中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu):在include/asm/arch/irq.h中定義。
irq_desc[]是一個(gè)指向irq_desc_t結(jié)構(gòu)的數(shù)組,irq_desc_t結(jié)構(gòu)是各個(gè)設(shè)備中斷服務(wù)例程的描述符。Irq_desc_t結(jié)構(gòu)體中的成員action指向該中斷號(hào)對(duì)應(yīng)的irqaction結(jié)構(gòu)體鏈表。Irqaction結(jié)構(gòu)體定義在include/linux/interrupt.h中,如下:
點(diǎn)擊(此處)折疊或打開(kāi)
- truct irqaction{
- irq_handler_t handler;//中斷處理函數(shù),注冊(cè)時(shí)提供
- unsigned long flags;//中斷標(biāo)志,注冊(cè)時(shí)提供
- cpumask_t mask;//中斷掩碼
- constchar*name;//中斷名稱
- void*dev_id;//設(shè)備id,本文后面部分介紹中斷共享時(shí)會(huì)詳細(xì)說(shuō)明這個(gè)參數(shù)的作用
- struct irqaction*next;//如果有中斷共享,則繼續(xù)執(zhí)行,
- intirq;//中斷號(hào),注冊(cè)時(shí)提供
- struct proc_dir_entry*dir;//指向IRQn相關(guān)的/proc/irq/n目錄的描述符
- };
在注冊(cè)中斷號(hào)為irq的中斷服務(wù)程序時(shí),系統(tǒng)會(huì)根據(jù)注冊(cè)參數(shù)封裝相應(yīng)的irqaction結(jié)構(gòu)體。并把中斷號(hào)為irq的irqaction結(jié)構(gòu)體寫(xiě)入irq_desc [irq]->action。這樣就把設(shè)備的中斷請(qǐng)求號(hào)與該設(shè)備的中斷服務(wù)例程irqaction聯(lián)系在一起了。當(dāng)CPU接收到中斷請(qǐng)求后,就可以根據(jù)中斷號(hào)通過(guò)irq_desc []找到該設(shè)備的中斷服務(wù)程序。
3.中斷共享的處理模型
共享中斷的不同設(shè)備的iqraction結(jié)構(gòu)體都會(huì)添加進(jìn)該中斷號(hào)對(duì)應(yīng)的irq_desc結(jié)構(gòu)體的action成員所指向的irqaction鏈表內(nèi)。當(dāng)內(nèi)核發(fā)生中斷時(shí),它會(huì)依次調(diào)用該鏈表內(nèi)所有的handler函數(shù)。因此,若驅(qū)動(dòng)程序需要使用共享中斷機(jī)制,其中斷處理函數(shù)必須有能力識(shí)別是否是自己的硬件產(chǎn)生了中斷。通常是通過(guò)讀取該硬件設(shè)備提供的中斷flag標(biāo)志位進(jìn)行判斷。也就是說(shuō)不是任何設(shè)備都可以做為中斷共享源的,它必須能夠通過(guò)的它的中斷flag判斷出是否發(fā)生了中斷。
中斷共享的注冊(cè)方法是:
int request_irq(unsigned int irq, irq_handler_t handler,IRQF_SHARED, const char *devname, void *dev_id)
很多權(quán)威資料中都提到,中斷共享注冊(cè)時(shí)的注冊(cè)函數(shù)中的dev_id參數(shù)是必不可少的,并且dev_id的值必須唯一。那么這里提供唯一的dev_id值的究竟是做什么用的?
根據(jù)我們前面中斷模型的知識(shí),可以看出發(fā)生中斷時(shí),內(nèi)核并不判斷究竟是共享中斷線上的哪個(gè)設(shè)備產(chǎn)生了中斷,它會(huì)循環(huán)執(zhí)行所有該中斷線上注冊(cè)的中斷處理函數(shù)(即irqaction->handler函數(shù))。因此irqaction->handler函數(shù)有責(zé)任識(shí)別出是否是自己的硬件設(shè)備產(chǎn)生了中斷,然后再執(zhí)行該中斷處理函數(shù)。通常是通過(guò)讀取該硬件設(shè)備提供的中斷flag標(biāo)志位進(jìn)行判斷。那既然kernel循環(huán)執(zhí)行該中斷線上注冊(cè)的所有irqaction->handler函數(shù),把識(shí)別究竟是哪個(gè)硬件設(shè)備產(chǎn)生了中斷這件事交給中斷處理函數(shù)本身去做,那request_irq的dev_id參數(shù)究竟是做什么用的?
很多資料中都建議將設(shè)備結(jié)構(gòu)指針作為dev_id參數(shù)。在中斷到來(lái)時(shí),迅速地根據(jù)硬件寄存器中的信息比照傳入的dev_id參數(shù)判斷是否是本設(shè)備的中斷,若不是,應(yīng)迅速返回。這樣的說(shuō)法沒(méi)有問(wèn)題,也是我們編程時(shí)都遵循的方法。但事實(shí)上并不能夠說(shuō)明為什么中斷共享必須要設(shè)置dev_id。
下面解釋一下dev_id參數(shù)為什么必須的,而且是必須唯一的。
當(dāng)調(diào)用free_irq注銷中斷處理函數(shù)時(shí)(通常卸載驅(qū)動(dòng)時(shí)其中斷處理函數(shù)也會(huì)被注銷掉),因?yàn)閐ev_id是唯一的,所以可以通過(guò)它來(lái)判斷從共享中斷線上的多個(gè)中斷處理程序中刪除指定的一個(gè)。如果沒(méi)有這個(gè)參數(shù),那么kernel不可能知道給定的中斷線上到底要?jiǎng)h除哪一個(gè)處理程序。
注銷函數(shù)定義在Kernel/irq/manage.c中定義:
void free_irq(unsigned int irq, void *dev_id)
4.S3C2410子中斷的注冊(cè)的實(shí)現(xiàn)
前面判斷中斷號(hào)的方法,可以看到只是通過(guò)S3C2410中斷控制器中的INTOFFSET寄存器來(lái)判斷的。對(duì)于INTPND中的EINT4_7、EINT8_23、INT_UART0、INT_ADC等帶有子中斷的向量,INTOFFSET無(wú)法判斷出具體的中斷號(hào)。平臺(tái)留給我們的注冊(cè)方法如下:
在include/asm/arch/irqs.h中有類似如下定義:
點(diǎn)擊(此處)折疊或打開(kāi)
- /*interrupts generated from the external interrupts sources*/
- #define IRQ_EINT4 S3C2410_IRQ(32)/*48*/
- #define IRQ_EINT5 S3C2410_IRQ(33)
- #define IRQ_EINT6 S3C2410_IRQ(34)
- #define IRQ_EINT7 S3C2410_IRQ(35)
- #define IRQ_EINT8 S3C2410_IRQ(36)
- #define IRQ_EINT9 S3C2410_IRQ(37)
- #define IRQ_EINT10 S3C2410_IRQ(38)
- #define IRQ_EINT11 S3C2410_IRQ(39)
- #define IRQ_EINT12 S3C2410_IRQ(40)
- #define IRQ_EINT13 S3C2410_IRQ(41)
- #define IRQ_EINT14 S3C2410_IRQ(42)
- #define IRQ_EINT15 S3C2410_IRQ(43)
- #define IRQ_EINT16 S3C2410_IRQ(44)
- #define IRQ_EINT17 S3C2410_IRQ(45)
- #define IRQ_EINT18 S3C2410_IRQ(46)
- #define IRQ_EINT19 S3C2410_IRQ(47)
- #define IRQ_EINT20 S3C2410_IRQ(48)/*64*/
- #define IRQ_EINT21 S3C2410_IRQ(49)
- #define IRQ_EINT22 S3C2410_IRQ(50)
- #define IRQ_EINT23 S3C2410_IRQ(51)
可以看到平臺(tái)為每種子中斷都定義了中斷號(hào),如果你想實(shí)現(xiàn)EINT10的中斷注冊(cè),直接按照IRQ_EINT10這個(gè)中斷號(hào)注冊(cè)都可以了。那么平臺(tái)代碼是如何實(shí)現(xiàn)這部分中斷注冊(cè)的呢?
5.S3C2410子中斷注冊(cè)問(wèn)題的解決
點(diǎn)擊(此處)折疊或打開(kāi)
- /*arch/arm/plat-s3c24xx/irq.c*/
- void __init s3c24xx_init_irq(void)
- {……
- set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7);
- set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8);
- set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0);
- set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1);
- set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2);
- set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc);
- ……
- }
平臺(tái)在初始化時(shí)會(huì)調(diào)用到s3c24xx_init_irq,在此函數(shù)中實(shí)現(xiàn)了對(duì)EINT4_7、EINT8_23、INT_UART0、INT_ADC等中斷的注冊(cè)。下面看看這些帶有子中斷的中斷號(hào)對(duì)應(yīng)的處理函數(shù)的內(nèi)容。以IRQ_EINT4t7為例,其它情況類似。
點(diǎn)擊(此處)折疊或打開(kāi)
- /*arch/arm/plat-s3c24xx/irq.c*/
- s3c_irq_demux_extint4t7(unsignedintirq,
- struct irq_desc*desc)
- {
- unsigned long eintpnd=__raw_readl(S3C24XX_EINTPEND);
- unsigned long eintmsk=__raw_readl(S3C24XX_EINTMASK);
- eintpnd&=~eintmsk;
- eintpnd&=0xff;/*only lower irqs*/
- /*eintpnd中可以有多個(gè)位同時(shí)置1,這一點(diǎn)和intpnd的只能有1個(gè)位置1是不一樣的*/
- while(eintpnd){//循環(huán)執(zhí)行所有置位的子中斷
- irq=__ffs(eintpnd);//算出第一個(gè)不為0的位,類似arm v5后的clz前導(dǎo)0的作用
- eintpnd&=~(1<
- irq+=(IRQ_EINT4-4);//算出對(duì)應(yīng)的中斷號(hào)
- desc_handle_irq(irq,irq_desc+irq);//執(zhí)行對(duì)應(yīng)子中斷的注冊(cè)函數(shù)
- }
- }
從上面的函數(shù)可以看出子中斷是如何注冊(cè)及被調(diào)用到的。有人可能會(huì)問(wèn)為何不在include/asm/arch-s3c2410/entry-macro.s文件中g(shù)et_irqnr_and_base函數(shù)判斷中斷號(hào)時(shí),直接算出對(duì)應(yīng)的子中斷號(hào),就可以直接找到子中斷處理了呢?
原因是: get_irqnr_and_base是平臺(tái)給系統(tǒng)提供的函數(shù),對(duì)于多個(gè)子中斷同時(shí)置位的情況無(wú)法通過(guò)一個(gè)值返回(因?yàn)樽又袛嘀?,如eintpnd是可以多個(gè)位同時(shí)置位的))。而intpnd則沒(méi)有這個(gè)問(wèn)題。
至此,對(duì)于s3c2440/10+linux2.6得出以下結(jié)論:
①不支持中斷嵌套(因?yàn)镕IQ不支持)
②有明確中斷優(yōu)先級(jí)(可編程)
③中斷號(hào)是根據(jù)硬件特性固定的,riq號(hào)通過(guò)某種轉(zhuǎn)換得到與寄存器相應(yīng)位,一般在irqs.h文件定義
中斷的用法見(jiàn)Ldd3的筆記:http://blog.chinaunix.net/uid-24708340-id-3035617.html
評(píng)論