一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Linux中斷(interrupt)子系統(tǒng)之二:arch相關(guān)的硬件封裝層

 寫意人生 2014-06-10

Linux的通用中斷子系統(tǒng)的一個(gè)設(shè)計(jì)原則就是把底層的硬件實(shí)現(xiàn)盡可能地隱藏起來(lái),使得驅(qū)動(dòng)程序的開發(fā)人員不用關(guān)注底層的實(shí)現(xiàn),要實(shí)現(xiàn)這個(gè)目標(biāo),內(nèi)核的開發(fā)者們必須把硬件相關(guān)的內(nèi)容剝離出來(lái),然后定義一些列標(biāo)準(zhǔn)的接口供上層訪問(wèn),上層的開發(fā)人員只要知道這些接口即可完成對(duì)中斷的進(jìn)一步處理和控制。對(duì)底層的封裝主要包括兩部分:

  • 實(shí)現(xiàn)不同體系結(jié)構(gòu)中斷入口,這部分代碼通常用asm實(shí)現(xiàn);
  • 中斷控制器進(jìn)行封裝和實(shí)現(xiàn);

本文的內(nèi)容正是要討論硬件封裝層的實(shí)現(xiàn)細(xì)節(jié)。我將以ARM體系進(jìn)行介紹,大部分的代碼位于內(nèi)核代碼樹的arch/arm/目錄內(nèi)。

/*****************************************************************************************************/
聲明:本博內(nèi)容均由http://blog.csdn.net/droidphone原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處,謝謝!
/*****************************************************************************************************/

1.  CPU的中斷入口

 我們知道,arm的異常和復(fù)位向量表有兩種選擇,一種是低端向量,向量地址位于0x00000000,另一種是高端向量,向量地址位于0xffff0000,Linux選擇使用高端向量模式,也就是說(shuō),當(dāng)異常發(fā)生時(shí),CPU會(huì)把PC指針自動(dòng)跳轉(zhuǎn)到始于0xffff0000開始的某一個(gè)地址上:

ARM的異常向量表
地址 異常種類
FFFF0000 復(fù)位
FFFF0004 未定義指令
FFFF0008 軟中斷(swi)
FFFF000C Prefetch abort
FFFF0010 Data abort
FFFF0014 保留
FFFF0018 IRQ
FFFF001C FIQ

中斷向量表在arch/arm/kernel/entry_armv.S中定義,為了方便討論,下面只列出部分關(guān)鍵的代碼:

  1.     .globl  __stubs_start  
  2. __stubs_start:  
  3.   
  4.     vector_stub irq, IRQ_MODE, 4  
  5.   
  6.     .long   __irq_usr           @  0  (USR_26 / USR_32)  
  7.     .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)  
  8.     .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)  
  9.     .long   __irq_svc           @  3  (SVC_26 / SVC_32)  
  10.   
  11.     vector_stub dabt, ABT_MODE, 8  
  12.   
  13.     .long   __dabt_usr          @  0  (USR_26 / USR_32)  
  14.     .long   __dabt_invalid          @  1  (FIQ_26 / FIQ_32)  
  15.     .long   __dabt_invalid          @  2  (IRQ_26 / IRQ_32)  
  16.     .long   __dabt_svc          @  3  (SVC_26 / SVC_32)  
  17.   
  18. vector_fiq:  
  19.     disable_fiq  
  20.     subs    pc, lr, #4  
  21.     ......  
  22.     .globl  __stubs_end  
  23. __stubs_end:  
  24.   
  25.   
  26.   
  27.     .equ    stubs_offset, __vectors_start + 0x200 - __stubs_start  
  28.     .globl  __vectors_start  
  29. __vectors_start:  
  30.  ARM(   swi SYS_ERROR0  )  
  31.  THUMB( svc #0      )  
  32.  THUMB( nop         )  
  33.     W(b)    vector_und + stubs_offset  
  34.     W(ldr)  pc, .LCvswi + stubs_offset  
  35.     W(b)    vector_pabt + stubs_offset  
  36.     W(b)    vector_dabt + stubs_offset  
  37.     W(b)    vector_addrexcptn + stubs_offset  
  38.     W(b)    vector_irq + stubs_offset  
  39.     W(b)    vector_fiq + stubs_offset  
  40.   
  41.     .globl  __vectors_end  
  42. __vectors_end:  
代碼被分為兩部分:

  • 第一部分是真正的向量跳轉(zhuǎn)表,位于__vectors_start和__vectors_end之間;
  • 第二部分是處理跳轉(zhuǎn)的部分,位于__stubs_start和__stubs_end之間;
  1. vector_stub irq, IRQ_MODE, 4  

以上這一句把宏展開后實(shí)際上就是定義了vector_irq,根據(jù)進(jìn)入中斷前的cpu模式,分別跳轉(zhuǎn)到__irq_usr或__irq_svc。

  1. vector_stub dabt, ABT_MODE, 8  
以上這一句把宏展開后實(shí)際上就是定義了vector_dabt,根據(jù)進(jìn)入中斷前的cpu模式,分別跳轉(zhuǎn)到__dabt_usr或__dabt_svc。


系統(tǒng)啟動(dòng)階段,位于arch/arm/kernel/traps.c中的early_trap_init()被調(diào)用:

  1. void __init early_trap_init(void)  
  2. {  
  3.     ......  
  4.     /* 
  5.      * Copy the vectors, stubs and kuser helpers (in entry-armv.S) 
  6.      * into the vector page, mapped at 0xffff0000, and ensure these 
  7.      * are visible to the instruction stream. 
  8.      */  
  9.     memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);  
  10.     memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);  
  11.     ......  
  12. }  
以上兩個(gè)memcpy會(huì)把__vectors_start開始的代碼拷貝到0xffff0000處,把__stubs_start開始的代碼拷貝到0xFFFF0000+0x200處,這樣,異常中斷到來(lái)時(shí),CPU就可以正確地跳轉(zhuǎn)到相應(yīng)中斷向量入口并執(zhí)行他們。

                                                                        圖1.1  Linux中ARM體系的中斷向量拷貝過(guò)程

對(duì)于系統(tǒng)的外部設(shè)備來(lái)說(shuō),通常都是使用IRQ中斷,所以我們只關(guān)注__irq_usr和__irq_svc,兩者的區(qū)別是進(jìn)入和退出中斷時(shí)是否進(jìn)行用戶棧和內(nèi)核棧之間的切換,還有進(jìn)程調(diào)度和搶占的處理等,這些細(xì)節(jié)不在這里討論。兩個(gè)函數(shù)最終都會(huì)進(jìn)入irq_handler這個(gè)宏:

  1.     .macro  irq_handler  
  2. #ifdef CONFIG_MULTI_IRQ_HANDLER  
  3.     ldr r1, =handle_arch_irq  
  4.     mov r0, sp  
  5.     adr lr, BSYM(9997f)  
  6.     ldr pc, [r1]  
  7. #else  
  8.     arch_irq_handler_default  
  9. #endif  
  10. 9997:  
  11.     .endm  
如果選擇了MULTI_IRQ_HANDLER配置項(xiàng),則意味著允許平臺(tái)的代碼可以動(dòng)態(tài)設(shè)置irq處理程序,平臺(tái)代碼可以修改全局變量:handle_arch_irq,從而可以修改irq的處理程序。這里我們討論默認(rèn)的實(shí)現(xiàn):arch_irq_handler_default,它位于arch/arm/include/asm/entry_macro_multi.S中:

  1.     .macro  arch_irq_handler_default  
  2.     get_irqnr_preamble r6, lr  
  3. 1:  get_irqnr_and_base r0, r2, r6, lr  
  4.     movne   r1, sp  
  5.     @  
  6.     @ routine called with r0 = irq number, r1 = struct pt_regs *  
  7.     @  
  8.     adrne   lr, BSYM(1b)  
  9.     bne asm_do_IRQ  
  10.     ......  

get_irqnr_preamble和get_irqnr_and_base兩個(gè)宏由machine級(jí)的代碼定義,目的就是從中斷控制器中獲得IRQ編號(hào),緊接著就調(diào)用asm_do_IRQ,從這個(gè)函數(shù)開始,中斷程序進(jìn)入C代碼中,傳入的參數(shù)是IRQ編號(hào)和寄存器結(jié)構(gòu)指針,這個(gè)函數(shù)在arch/arm/kernel/irq.c中實(shí)現(xiàn):

  1. /* 
  2.  * asm_do_IRQ is the interface to be used from assembly code. 
  3.  */  
  4. asmlinkage void __exception_irq_entry  
  5. asm_do_IRQ(unsigned int irq, struct pt_regs *regs)  
  6. {  
  7.     handle_IRQ(irq, regs);  
  8. }  
到這里,中斷程序完成了從asm代碼到C代碼的傳遞,并且獲得了引起中斷的IRQ編號(hào)。

2.  初始化

與通用中斷子系統(tǒng)相關(guān)的初始化由start_kernel()函數(shù)發(fā)起,調(diào)用流程如下圖所視:

                                                                           圖2.1  通用中斷子系統(tǒng)的初始化

  • 首先,在setup_arch函數(shù)中,early_trap_init被調(diào)用,其中完成了第1節(jié)所說(shuō)的中斷向量的拷貝和重定位工作。
  • 然后,start_kernel發(fā)出early_irq_init調(diào)用,early_irq_init屬于與硬件和平臺(tái)無(wú)關(guān)的通用邏輯層,它完成irq_desc結(jié)構(gòu)的內(nèi)存申請(qǐng),為它們其中某些字段填充默認(rèn)值,完成后調(diào)用體系相關(guān)的arch_early_irq_init函數(shù)完成進(jìn)一步的初始化工作,不過(guò)ARM體系沒(méi)有實(shí)現(xiàn)arch_early_irq_init。
  • 接著,start_kernel發(fā)出init_IRQ調(diào)用,它會(huì)直接調(diào)用所屬板子machine_desc結(jié)構(gòu)體中的init_irq回調(diào)。machine_desc通常在板子的特定代碼中,使用MACHINE_START和MACHINE_END宏進(jìn)行定義。
  • machine_desc->init_irq()完成對(duì)中斷控制器的初始化,為每個(gè)irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個(gè)irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對(duì)應(yīng)的irq_chip結(jié)構(gòu)的實(shí)例,同時(shí),如果該平臺(tái)中的中斷線有多路復(fù)用(多個(gè)中斷公用一個(gè)irq中斷線)的情況,還應(yīng)該初始化irq_desc中相應(yīng)的字段和標(biāo)志,以便實(shí)現(xiàn)中斷控制器的級(jí)聯(lián)。

3.  中斷控制器的軟件抽象:struct irq_chip

正如上一篇文章Linux中斷(interrupt)子系統(tǒng)之一:中斷系統(tǒng)基本原理所述,所有的硬件中斷在到達(dá)CPU之前,都要先經(jīng)過(guò)中斷控制器進(jìn)行匯集,合乎要求的中斷請(qǐng)求才會(huì)通知cpu進(jìn)行處理,中斷控制器主要完成以下這些功能:

  • 對(duì)各個(gè)irq的優(yōu)先級(jí)進(jìn)行控制;
  • 向CPU發(fā)出中斷請(qǐng)求后,提供某種機(jī)制讓CPU獲得實(shí)際的中斷源(irq編號(hào));
  • 控制各個(gè)irq的電氣觸發(fā)條件,例如邊緣觸發(fā)或者是電平觸發(fā);
  • 使能(enable)或者屏蔽(mask)某一個(gè)irq;
  • 提供嵌套中斷請(qǐng)求的能力;
  • 提供清除中斷請(qǐng)求的機(jī)制(ack);
  • 有些控制器還需要CPU在處理完irq后對(duì)控制器發(fā)出eoi指令(end of interrupt);
  • 在smp系統(tǒng)中,控制各個(gè)irq與cpu之間的親緣關(guān)系(affinity);

通用中斷子系統(tǒng)把中斷控制器抽象為一個(gè)數(shù)據(jù)結(jié)構(gòu):struct irq_chip,其中定義了一系列的操作函數(shù),大部分多對(duì)應(yīng)于上面所列的某個(gè)功能:

  1. struct irq_chip {  
  2.     const char  *name;  
  3.     unsigned int    (*irq_startup)(struct irq_data *data);  
  4.     void        (*irq_shutdown)(struct irq_data *data);  
  5.     void        (*irq_enable)(struct irq_data *data);  
  6.     void        (*irq_disable)(struct irq_data *data);  
  7.   
  8.     void        (*irq_ack)(struct irq_data *data);  
  9.     void        (*irq_mask)(struct irq_data *data);  
  10.     void        (*irq_mask_ack)(struct irq_data *data);  
  11.     void        (*irq_unmask)(struct irq_data *data);  
  12.     void        (*irq_eoi)(struct irq_data *data);  
  13.   
  14.     int     (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);  
  15.     int     (*irq_retrigger)(struct irq_data *data);  
  16.     int     (*irq_set_type)(struct irq_data *data, unsigned int flow_type);  
  17.     int     (*irq_set_wake)(struct irq_data *data, unsigned int on);  
  18.   
  19.     void        (*irq_bus_lock)(struct irq_data *data);  
  20.     void        (*irq_bus_sync_unlock)(struct irq_data *data);  
  21.   
  22.     void        (*irq_cpu_online)(struct irq_data *data);  
  23.     void        (*irq_cpu_offline)(struct irq_data *data);  
  24.   
  25.     void        (*irq_suspend)(struct irq_data *data);  
  26.     void        (*irq_resume)(struct irq_data *data);  
  27.     void        (*irq_pm_shutdown)(struct irq_data *data);  
  28.   
  29.     void        (*irq_print_chip)(struct irq_data *data, struct seq_file *p);  
  30.   
  31.     unsigned long   flags;  
  32.   
  33.     /* Currently used only by UML, might disappear one day.*/  
  34. #ifdef CONFIG_IRQ_RELEASE_METHOD  
  35.     void        (*release)(unsigned int irq, void *dev_id);  
  36. #endif  
  37. };  

各個(gè)字段解釋如下:


name  中斷控制器的名字,會(huì)出現(xiàn)在 /proc/interrupts中。

irq_startup  第一次開啟一個(gè)irq時(shí)使用。

irq_shutdown  與irq_starup相對(duì)應(yīng)。

irq_enable  使能該irq,通常是直接調(diào)用irq_unmask()。

irq_disable  禁止該irq,通常是直接調(diào)用irq_mask,嚴(yán)格意義上,他倆其實(shí)代表不同的意義,disable表示中斷控制器根本就不響應(yīng)該irq,而mask時(shí),中斷控制器可能響應(yīng)該irq,只是不通知CPU,這時(shí),該irq處于pending狀態(tài)。類似的區(qū)別也適用于enable和unmask。

irq_ack  用于CPU對(duì)該irq的回應(yīng),通常表示cpu希望要清除該irq的pending狀態(tài),準(zhǔn)備接受下一個(gè)irq請(qǐng)求。

irq_mask  屏蔽該irq。

irq_unmask  取消屏蔽該irq。

irq_mask_ack  相當(dāng)于irq_mask + irq_ack。

irq_eoi  有些中斷控制器需要在cpu處理完該irq后發(fā)出eoi信號(hào),該回調(diào)就是用于這個(gè)目的。

irq_set_affinity  用于設(shè)置該irq和cpu之間的親緣關(guān)系,就是通知中斷控制器,該irq發(fā)生時(shí),那些cpu有權(quán)響應(yīng)該irq。當(dāng)然,中斷控制器會(huì)在軟件的配合下,最終只會(huì)讓一個(gè)cpu處理本次請(qǐng)求。

irq_set_type  設(shè)置irq的電氣觸發(fā)條件,例如IRQ_TYPE_LEVEL_HIGH或IRQ_TYPE_EDGE_RISING。

irq_set_wake  通知電源管理子系統(tǒng),該irq是否可以用作系統(tǒng)的喚醒源。

以上大部分的函數(shù)接口的參數(shù)都是irq_data結(jié)構(gòu)指針,irq_data結(jié)構(gòu)的由來(lái)在上一篇文章已經(jīng)說(shuō)過(guò),這里僅貼出它的定義,各字段的意義請(qǐng)參考注釋:

  1. /** 
  2.  * struct irq_data - per irq and irq chip data passed down to chip functions 
  3.  * @irq:        interrupt number 
  4.  * @hwirq:      hardware interrupt number, local to the interrupt domain 
  5.  * @node:       node index useful for balancing 
  6.  * @state_use_accessors: status information for irq chip functions. 
  7.  *          Use accessor functions to deal with it 
  8.  * @chip:       low level interrupt hardware access 
  9.  * @domain:     Interrupt translation domain; responsible for mapping 
  10.  *          between hwirq number and linux irq number. 
  11.  * @handler_data:   per-IRQ data for the irq_chip methods 
  12.  * @chip_data:      platform-specific per-chip private data for the chip 
  13.  *          methods, to allow shared chip implementations 
  14.  * @msi_desc:       MSI descriptor 
  15.  * @affinity:       IRQ affinity on SMP 
  16.  * 
  17.  * The fields here need to overlay the ones in irq_desc until we 
  18.  * cleaned up the direct references and switched everything over to 
  19.  * irq_data. 
  20.  */  
  21. struct irq_data {  
  22.     unsigned int        irq;  
  23.     unsigned long       hwirq;  
  24.     unsigned int        node;  
  25.     unsigned int        state_use_accessors;  
  26.     struct irq_chip     *chip;  
  27.     struct irq_domain   *domain;  
  28.     void            *handler_data;  
  29.     void            *chip_data;  
  30.     struct msi_desc     *msi_desc;  
  31. #ifdef CONFIG_SMP  
  32.     cpumask_var_t       affinity;  
  33. #endif  
  34. };  

根據(jù)設(shè)備使用的中斷控制器的類型,體系架構(gòu)的底層的開發(fā)只要實(shí)現(xiàn)上述接口中的各個(gè)回調(diào)函數(shù),然后把它們填充到irq_chip結(jié)構(gòu)的實(shí)例中,最終把該irq_chip實(shí)例注冊(cè)到irq_desc.irq_data.chip字段中,這樣各個(gè)irq和中斷控制器就進(jìn)行了關(guān)聯(lián),只要知道irq編號(hào),即可得到對(duì)應(yīng)到irq_desc結(jié)構(gòu),進(jìn)而可以通過(guò)chip指針訪問(wèn)中斷控制器。 

4.  進(jìn)入流控處理層

進(jìn)入C代碼的第一個(gè)函數(shù)是asm_do_IRQ,在ARM體系中,這個(gè)函數(shù)只是簡(jiǎn)單地調(diào)用handle_IRQ:

  1. /* 
  2.  * asm_do_IRQ is the interface to be used from assembly code. 
  3.  */  
  4. asmlinkage void __exception_irq_entry  
  5. asm_do_IRQ(unsigned int irq, struct pt_regs *regs)  
  6. {  
  7.     handle_IRQ(irq, regs);  
  8. }  
handle_IRQ本身也不是很復(fù)雜:

  1. void handle_IRQ(unsigned int irq, struct pt_regs *regs)  
  2. {  
  3.     struct pt_regs *old_regs = set_irq_regs(regs);  
  4.   
  5.     irq_enter();  
  6.   
  7.     /* 
  8.      * Some hardware gives randomly wrong interrupts.  Rather 
  9.      * than crashing, do something sensible. 
  10.      */  
  11.     if (unlikely(irq >= nr_irqs)) {  
  12.         if (printk_ratelimit())  
  13.             printk(KERN_WARNING "Bad IRQ%u\n", irq);  
  14.         ack_bad_irq(irq);  
  15.     } else {  
  16.         generic_handle_irq(irq);  
  17.     }  
  18.   
  19.     /* AT91 specific workaround */  
  20.     irq_finish(irq);  
  21.   
  22.     irq_exit();  
  23.     set_irq_regs(old_regs);  
  24. }  
irq_enter主要是更新一些系統(tǒng)的統(tǒng)計(jì)信息,同時(shí)在__irq_enter宏中禁止了進(jìn)程的搶占:

  1. #define __irq_enter()                   \  
  2.     do {                        \  
  3.         account_system_vtime(current);      \  
  4.         add_preempt_count(HARDIRQ_OFFSET);  \  
  5.         trace_hardirq_enter();          \  
  6.     } while (0)  
CPU一旦響應(yīng)IRQ中斷后,ARM會(huì)自動(dòng)把CPSR中的I位置位,表明禁止新的IRQ請(qǐng)求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過(guò)local_irq_enable()打開。你可能會(huì)奇怪,既然此時(shí)的irq中斷都是都是被禁止的,為何還要禁止搶占?這是因?yàn)橐紤]中斷嵌套的問(wèn)題,一旦流控層或驅(qū)動(dòng)程序主動(dòng)通過(guò)local_irq_enable打開了IRQ,而此時(shí)該中斷還沒(méi)處理完成,新的irq請(qǐng)求到達(dá),這時(shí)代碼會(huì)再次進(jìn)入irq_enter,在本次嵌套中斷返回時(shí),內(nèi)核不希望進(jìn)行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動(dòng)作,所以才有了禁止搶占這一處理。

下一步,generic_handle_irq被調(diào)用,generic_handle_irq是通用邏輯層提供的API,通過(guò)該API,中斷的控制被傳遞到了與體系結(jié)構(gòu)無(wú)關(guān)的中斷流控層:

  1. int generic_handle_irq(unsigned int irq)  
  2. {  
  3.     struct irq_desc *desc = irq_to_desc(irq);  
  4.   
  5.     if (!desc)  
  6.         return -EINVAL;  
  7.     generic_handle_irq_desc(irq, desc);  
  8.     return 0;  
  9. }  
最終會(huì)進(jìn)入該irq注冊(cè)的流控處理回調(diào)中:

  1. static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)  
  2. {  
  3.     desc->handle_irq(irq, desc);  
  4. }  


5.  中斷控制器的級(jí)聯(lián)

在實(shí)際的設(shè)備中,經(jīng)常存在多個(gè)中斷控制器,有時(shí)多個(gè)中斷控制器還會(huì)進(jìn)行所謂的級(jí)聯(lián)。為了方便討論,我們把直接和CPU相連的中斷控制器叫做根控制器,另外一些和跟控制器相連的叫子控制器。根據(jù)子控制器的位置,我們把它們分為兩種類型:
  • 機(jī)器級(jí)別的級(jí)聯(lián)  子控制器位于SOC內(nèi)部,或者子控制器在SOC的外部,但是是某個(gè)板子系列的標(biāo)準(zhǔn)配置,如圖5.1的左邊所示;
  • 設(shè)備級(jí)別的級(jí)聯(lián)  子控制器位于某個(gè)外部設(shè)備中,用于匯集該設(shè)備發(fā)出的多個(gè)中斷,如圖5.1的右邊所示;

 

                                                                                 圖5.1  中斷控制器的級(jí)聯(lián)類型

對(duì)于機(jī)器級(jí)別的級(jí)聯(lián),級(jí)聯(lián)的初始化代碼理所當(dāng)然地位于板子的初始化代碼中(arch/xxx/mach-xxx),因?yàn)橹灰鞘褂眠@個(gè)板子或SOC的設(shè)備,必然要使用這個(gè)子控制器。而對(duì)于設(shè)備級(jí)別的級(jí)聯(lián),因?yàn)樵撛O(shè)備并不一定是系統(tǒng)的標(biāo)配設(shè)備,所以中斷控制器的級(jí)聯(lián)操作應(yīng)該在該設(shè)備的驅(qū)動(dòng)程序中實(shí)現(xiàn)。機(jī)器設(shè)備的級(jí)聯(lián),因?yàn)榈靡嬗谑孪纫呀?jīng)知道子控制器的硬件連接信息,內(nèi)核可以方便地為子控制器保留相應(yīng)的irq_desc結(jié)構(gòu)和irq編號(hào),處理起來(lái)相對(duì)簡(jiǎn)單。設(shè)備級(jí)別的級(jí)聯(lián)則不一樣,驅(qū)動(dòng)程序必須動(dòng)態(tài)地決定組合設(shè)備中各個(gè)子設(shè)備的irq編號(hào)和irq_desc結(jié)構(gòu)。本章我只討論機(jī)器級(jí)別的級(jí)聯(lián),設(shè)備級(jí)別的關(guān)聯(lián)可以使用同樣的原理,也可以實(shí)現(xiàn)為共享中斷,我會(huì)在本系列接下來(lái)的文章中討論。

要實(shí)現(xiàn)中斷控制器的級(jí)聯(lián),要使用以下幾個(gè)的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)字段和通用中斷邏輯層的API:

        irq_desc.handle_irq  irq的流控處理回調(diào)函數(shù),子控制器在把多個(gè)irq匯集起來(lái)后,輸出端連接到根控制器的其中一個(gè)irq中斷線輸入腳,這意味著,每個(gè)子控制器的中斷發(fā)生時(shí),CPU一開始只會(huì)得到根控制器的irq編號(hào),然后進(jìn)入該irq編號(hào)對(duì)應(yīng)的irq_desc.handle_irq回調(diào),該回調(diào)我們不能使用流控層定義好的幾個(gè)流控函數(shù),而是要自己實(shí)現(xiàn)一個(gè)函數(shù),該函數(shù)負(fù)責(zé)從子控制器中獲得irq的中斷源,并計(jì)算出對(duì)應(yīng)新的irq編號(hào),然后調(diào)用新irq所對(duì)應(yīng)的irq_desc.handle_irq回調(diào),這個(gè)回調(diào)使用流控層的標(biāo)準(zhǔn)實(shí)現(xiàn)。

        irq_set_chained_handler()  該API用于設(shè)置根控制器與子控制器相連的irq所對(duì)應(yīng)的irq_desc.handle_irq回調(diào)函數(shù),并且設(shè)置IRQ_NOPROBE和IRQ_NOTHREAD以及IRQ_NOREQUEST標(biāo)志,這幾個(gè)標(biāo)志保證驅(qū)動(dòng)程序不會(huì)錯(cuò)誤地申請(qǐng)?jiān)搃rq,因?yàn)樵搃rq已經(jīng)被作為級(jí)聯(lián)irq使用。

        irq_set_chip_and_handler()  該API同時(shí)設(shè)置irq_desc中的handle_irq回調(diào)和irq_chip指針。

以下例子代碼位于:/arch/arm/plat-s5p/irq-eint.c:

  1. int __init s5p_init_irq_eint(void)  
  2. {  
  3.     int irq;  
  4.   
  5.     for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)  
  6.         irq_set_chip(irq, &s5p_irq_vic_eint);  
  7.   
  8.     for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {  
  9.         irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);  
  10.         set_irq_flags(irq, IRQF_VALID);  
  11.     }  
  12.   
  13.     irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);  
  14.     return 0;  
  15. }  

該SOC芯片的外部中斷:IRQ_EINT(0)到IRQ_EINT(15),每個(gè)引腳對(duì)應(yīng)一個(gè)根控制器的irq中斷線,它們是正常的irq,無(wú)需級(jí)聯(lián)。IRQ_EINT(16)到IRQ_EINT(31)經(jīng)過(guò)子控制器匯集后,統(tǒng)一連接到根控制器編號(hào)為IRQ_EINT16_31這個(gè)中斷線上??梢钥吹剑涌刂破鲗?duì)應(yīng)的irq_chip是s5p_irq_eint,子控制器的irq默認(rèn)設(shè)置為電平中斷的流控處理函數(shù)handle_level_irq,它們通過(guò)API:irq_set_chained_handler進(jìn)行設(shè)置。如果根控制器有128個(gè)中斷線,IRQ_EINT0--IRQ_EINT15通常占據(jù)128內(nèi)的某段連續(xù)范圍,這取決于實(shí)際的物理連接。IRQ_EINT16_31因?yàn)橐矊儆诟刂破?,所以它的值也?huì)位于128以內(nèi),但是IRQ_EINT16--IRQ_EINT31通常會(huì)在128以外的某段范圍,這時(shí),代表irq數(shù)量的常量NR_IRQS,必須考慮這種情況,定義出超過(guò)128的某個(gè)足夠的數(shù)值。級(jí)聯(lián)的實(shí)現(xiàn)主要依靠編號(hào)為IRQ_EINT16_31的流控處理程序:s5p_irq_demux_eint16_31,它的最終實(shí)現(xiàn)類似于以下代碼:

  1. static inline void s5p_irq_demux_eint(unsigned int start)  
  2. {  
  3.     u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));  
  4.     u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));  
  5.     unsigned int irq;  
  6.   
  7.     status &= ~mask;  
  8.     status &= 0xff;  
  9.   
  10.     while (status) {  
  11.         irq = fls(status) - 1;  
  12.         generic_handle_irq(irq + start);  
  13.         status &= ~(1 << irq);  
  14.     }  
  15. }  

在獲得新的irq編號(hào)后,它的最關(guān)鍵的一句是調(diào)用了通用中斷邏輯層的API:generic_handle_irq,這時(shí)它才真正地把中斷控制權(quán)傳遞到中斷流控層中來(lái)。

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲国产av一二三区| 国产成人av在线免播放观看av | 精品女同在线一区二区| 国产又黄又爽又粗视频在线| 激情综合网俺也狠狠地| 日本人妻中出在线观看| 毛片在线观看免费日韩| 午夜精品国产精品久久久| 日韩精品一级片免费看| 亚洲av秘片一区二区三区| 亚洲一区二区久久观看| 日韩中文字幕视频在线高清版| 国产精品福利一二三区| 麻豆欧美精品国产综合久久| 久久精品国产一区久久久| 在线观看视频日韩精品| 亚洲妇女黄色三级视频| 99精品国产自在现线观看| 国产精品成人免费精品自在线观看| 国产视频在线一区二区| 欧美午夜性刺激在线观看| 中文字幕亚洲精品在线播放| 五月婷婷六月丁香在线观看| 亚洲欧美国产中文色妇| 日本在线视频播放91| 欧美小黄片在线一级观看| 人妻少妇av中文字幕乱码高清| 久久午夜福利精品日韩| 亚洲欧美日韩国产综合在线| 2019年国产最新视频| 亚洲一二三四区免费视频| 色婷婷国产熟妇人妻露脸| 不卡一区二区在线视频| 在线视频三区日本精品| 亚洲日本久久国产精品久久| 日韩中文字幕狠狠人妻| 偷拍偷窥女厕一区二区视频 | 欧美日韩中黄片免费看| 亚洲欧美国产中文色妇| 欧美日韩国产精品自在自线| 欧美日韩有码一二三区|