新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM+Linux中斷系統(tǒng)詳細(xì)分析

ARM+Linux中斷系統(tǒng)詳細(xì)分析

作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
ULK第四章里明確講到“Linux實(shí)現(xiàn)了一種沒(méi)有優(yōu)先級(jí)的中斷模型”,并且“Linux中斷和異常都支持嵌套”。這個(gè)我不太理解了,這兩種說(shuō)法都與我以前的理解剛好相反,核對(duì)了原書(shū),翻譯沒(méi)有錯(cuò)。

Linux中斷系統(tǒng)到底是否支持優(yōu)先級(jí),可否嵌套,中斷號(hào)又是怎么來(lái)確定的,中斷產(chǎn)生時(shí)又是如何一步步執(zhí)行到中斷處理函數(shù)的。為了徹底搞懂Linux中斷系統(tǒng),我決定從最原始材料出發(fā),一探究竟。(s3c2440+linux2.6.21)

本文引用地址:http://m.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)

  1. void __init trap_init(void)
  2. {
  3. unsigned long vectors=CONFIG_VECTORS_BASE;
  4. memcpy((void*)vectors,__vectors_start,__vectors_end-__vectors_start);
  5. memcpy((void*)vectors+0x200,__stubs_start,__stubs_end-__stubs_start);
  6. memcpy((void*)vectors+0x1000-kuser_sz,__kuser_helper_start,kuser_sz);
  7. }
  8. #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)

  1. .globl __vectors_start
  2. __vectors_start:
  3. swi SYS_ERROR0
  4. b vector_und + stubs_offset//復(fù)位異常
  5. ldr pc, .LCvswi + stubs_offset //未定義異常
  6. b vector_pabt + stubs_offset//軟件中斷異常
  7. b vector_dabt + stubs_offset//數(shù)據(jù)異常
  8. b vector_addrexcptn + stubs_offset//保留
  9. b vector_irq + stubs_offset //普通中斷異常
  10. b vector_fiq + stubs_offset//快速中斷異常
  11. .globl __vectors_end
  12. __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)

  1. .globl __stubs_start
  2. __stubs_start:
  3. /*
  4. * Interrupt dispatcher
  5. */
  6. vector_stub irq, IRQ_MODE, 4
  7. .long __irq_usr @ 0 (USR_26 / USR_32)
  8. .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
  9. .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
  10. .long __irq_svc @ 3 (SVC_26 / SVC_32)
  11. vector_stub dabt, ABT_MODE, 8
  12. vector_stub pabt, ABT_MODE, 4
  13. vector_stub und, UND_MODE
  14. /*
  15. * Undefined FIQs
  16. */
  17. vector_fiq:
  18. disable_fiq
  19. subs pc, lr, #4
  20. vector_addrexcptn:
  21. 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)

  1. __irq_svc:
  2. svc_entry
  3. 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)

  1. .macro irq_handler
  2. get_irqnr_preamble r5, lr
  3. 1: get_irqnr_and_base r0, r6, r5, lr @判斷中斷號(hào),通過(guò)R0返回,3.5節(jié)有實(shí)現(xiàn)過(guò)程
  4. movne r1, sp
  5. @
  6. @ routine called with r0 = irq number, r1 = struct pt_regs *
  7. @
  8. adrne lr, 1b
  9. bne asm_do_IRQ @進(jìn)入中斷處理。
  10. ……
  11. .endm

get_irqnr_and_base中斷號(hào)判斷過(guò)程,include/asm/arch-s3c2410/entry-macro.s

點(diǎn)擊(此處)折疊或打開(kāi)

  1. .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
  2. mov base, #S3C24XX_VA_IRQ
  3. @@ try the interrupt offset register, since it is there
  4. ldr irqstat, [ base, #INTPND ]
  5. teq irqstat, #0
  6. beq 1002f
  7. ldr irqnr, [ base, #INTOFFSET ] @通過(guò)判斷INTOFFSET寄存器得到中斷位置
  8. @@ we have the value
  9. 1001:
  10. 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)是不等的。
  11. 1002:
  12. @@ exit here, Z flag unset if IRQ
  13. .endm

asm_do_IRQ實(shí)現(xiàn)過(guò)程,arch/arm/kernel/irq.c

點(diǎn)擊(此處)折疊或打開(kāi)

  1. asmlinkage void asm_do_IRQ(unsignedintirq,struct pt_regs*regs)
  2. {
  3. struct pt_regs*old_regs=set_irq_regs(regs);
  4. struct irq_desc*desc=irq_desc+irq;//根據(jù)中斷號(hào),找到響應(yīng)的irq_desc
  5. /*
  6. *Some hardware gives randomly wrong interrupts.Rather
  7. *than crashing,dosomething sensible.
  8. */
  9. if(irq>=NR_IRQS)
  10. desc=&bad_irq_desc;
  11. irq_enter();
  12. desc_handle_irq(irq,desc);//根據(jù)irq和desc進(jìn)入中斷處理
  13. /*AT91 specific workaround*/
  14. irq_finish(irq);
  15. irq_exit();
  16. set_irq_regs(old_regs);
  17. }
  18. static inline void desc_handle_irq(unsignedintirq,struct irq_desc*desc)
  19. {
  20. desc->handle_irq(irq,desc);//中斷處理
  21. }

上述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中,如下所示:

#include //各個(gè)具體處理器在此文件中定義asmlinkage

點(diǎn)擊(此處)折疊或打開(kāi)

  1. #ifdef __cplusplus
  2. #define CPP_ASMLINKAGE extern"C"
  3. #else
  4. #define CPP_ASMLINKAGE
  5. #endif
  6. #ifndef asmlinkage//如果以前沒(méi)有定義asmlinkage
  7. #define asmlinkage CPP_ASMLINKAGE
  8. #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)

  1. truct irqaction{
  2. irq_handler_t handler;//中斷處理函數(shù),注冊(cè)時(shí)提供
  3. unsigned long flags;//中斷標(biāo)志,注冊(cè)時(shí)提供
  4. cpumask_t mask;//中斷掩碼
  5. constchar*name;//中斷名稱
  6. void*dev_id;//設(shè)備id,本文后面部分介紹中斷共享時(shí)會(huì)詳細(xì)說(shuō)明這個(gè)參數(shù)的作用
  7. struct irqaction*next;//如果有中斷共享,則繼續(xù)執(zhí)行,
  8. intirq;//中斷號(hào),注冊(cè)時(shí)提供
  9. struct proc_dir_entry*dir;//指向IRQn相關(guān)的/proc/irq/n目錄的描述符
  10. };

在注冊(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)

  1. /*interrupts generated from the external interrupts sources*/
  2. #define IRQ_EINT4 S3C2410_IRQ(32)/*48*/
  3. #define IRQ_EINT5 S3C2410_IRQ(33)
  4. #define IRQ_EINT6 S3C2410_IRQ(34)
  5. #define IRQ_EINT7 S3C2410_IRQ(35)
  6. #define IRQ_EINT8 S3C2410_IRQ(36)
  7. #define IRQ_EINT9 S3C2410_IRQ(37)
  8. #define IRQ_EINT10 S3C2410_IRQ(38)
  9. #define IRQ_EINT11 S3C2410_IRQ(39)
  10. #define IRQ_EINT12 S3C2410_IRQ(40)
  11. #define IRQ_EINT13 S3C2410_IRQ(41)
  12. #define IRQ_EINT14 S3C2410_IRQ(42)
  13. #define IRQ_EINT15 S3C2410_IRQ(43)
  14. #define IRQ_EINT16 S3C2410_IRQ(44)
  15. #define IRQ_EINT17 S3C2410_IRQ(45)
  16. #define IRQ_EINT18 S3C2410_IRQ(46)
  17. #define IRQ_EINT19 S3C2410_IRQ(47)
  18. #define IRQ_EINT20 S3C2410_IRQ(48)/*64*/
  19. #define IRQ_EINT21 S3C2410_IRQ(49)
  20. #define IRQ_EINT22 S3C2410_IRQ(50)
  21. #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)

  1. /*arch/arm/plat-s3c24xx/irq.c*/
  2. void __init s3c24xx_init_irq(void)
  3. {……
  4. set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7);
  5. set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8);
  6. set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0);
  7. set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1);
  8. set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2);
  9. set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc);
  10. ……
  11. }

平臺(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)

  1. /*arch/arm/plat-s3c24xx/irq.c*/
  2. s3c_irq_demux_extint4t7(unsignedintirq,
  3. struct irq_desc*desc)
  4. {
  5. unsigned long eintpnd=__raw_readl(S3C24XX_EINTPEND);
  6. unsigned long eintmsk=__raw_readl(S3C24XX_EINTMASK);
  7. eintpnd&=~eintmsk;
  8. eintpnd&=0xff;/*only lower irqs*/
  9. /*eintpnd中可以有多個(gè)位同時(shí)置1,這一點(diǎn)和intpnd的只能有1個(gè)位置1是不一樣的*/
  10. while(eintpnd){//循環(huán)執(zhí)行所有置位的子中斷
  11. irq=__ffs(eintpnd);//算出第一個(gè)不為0的位,類似arm v5后的clz前導(dǎo)0的作用
  12. eintpnd&=~(1<
  13. irq+=(IRQ_EINT4-4);//算出對(duì)應(yīng)的中斷號(hào)
  14. desc_handle_irq(irq,irq_desc+irq);//執(zhí)行對(duì)應(yīng)子中斷的注冊(cè)函數(shù)
  15. }
  16. }

從上面的函數(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



關(guān)鍵詞: ARMLinux中斷系

評(píng)論


技術(shù)專區(qū)

關(guān)閉