Linux的通用中斷子系統(tǒng)的一個設(shè)計原則就是把底層的硬件實現(xiàn)盡可能地隱藏起來,使得驅(qū)動程序的開發(fā)人員不用關(guān)注底層的實現(xiàn),要實現(xiàn)這個目標(biāo),內(nèi)核的開發(fā)者們必須把硬件相關(guān)的內(nèi)容剝離出來,然后定義一些列標(biāo)準(zhǔn)的接口供上層訪問,上層的開發(fā)人員只要知道這些接口即可完成對中斷的進一步處理和控制。對底層的封裝主要包括兩部分:
實現(xiàn)不同體系結(jié)構(gòu)中斷入口,這部分代碼通常用asm實現(xiàn);
中斷控制器進行封裝和實現(xiàn);
本文的內(nèi)容正是要討論硬件封裝層的實現(xiàn)細節(jié)。我將以ARM體系進行介紹,大部分的代碼位于內(nèi)核代碼樹的arch/arm/目錄內(nèi)。
1.? CPU的中斷入口
我們知道,arm的異常和復(fù)位向量表有兩種選擇,一種是低端向量,向量地址位于0x00000000,另一種是高端向量,向量地址位于0xffff0000,Linux選擇使用高端向量模式,也就是說,當(dāng)異常發(fā)生時,CPU會把PC指針自動跳轉(zhuǎn)到始于0xffff0000開始的某一個地址上:
ARM的異常向量表地址異常種類FFFF0000復(fù)位FFFF0004未定義指令FFFF0008軟中斷(swi)FFFF000CPrefetch abortFFFF0010Data abortFFFF0014保留FFFF0018IRQFFFF001CFIQ
中斷向量表在arch/arm/kernel/entry_armv.S中定義,為了方便討論,下面只列出部分關(guān)鍵的代碼:
[plain]?view plain?copy
.globl??__stubs_start??
__stubs_start:??
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??
.long???__dabt_usr??????????@??0??(USR_26?/?USR_32)??
.long???__dabt_invalid??????????@??1??(FIQ_26?/?FIQ_32)??
.long???__dabt_invalid??????????@??2??(IRQ_26?/?IRQ_32)??
.long???__dabt_svc??????????@??3??(SVC_26?/?SVC_32)??
vector_fiq:??
disable_fiq??
subs????pc,?lr,?#4??
......??
.globl??__stubs_end??
__stubs_end:??
.equ????stubs_offset,?__vectors_start?+?0x200?-?__stubs_start??
.globl??__vectors_start??
__vectors_start:??
ARM(???swi?SYS_ERROR0??)??
THUMB(?svc?#0??????)??
THUMB(?nop?????????)??
W(b)????vector_und?+?stubs_offset??
W(ldr)??pc,?.LCvswi?+?stubs_offset??
W(b)????vector_pabt?+?stubs_offset??
W(b)????vector_dabt?+?stubs_offset??
W(b)????vector_addrexcptn?+?stubs_offset??
W(b)????vector_irq?+?stubs_offset??
W(b)????vector_fiq?+?stubs_offset??
.globl??__vectors_end??
__vectors_end:??
代碼被分為兩部分:
第一部分是真正的向量跳轉(zhuǎn)表,位于__vectors_start和__vectors_end之間;
第二部分是處理跳轉(zhuǎn)的部分,位于__stubs_start和__stubs_end之間;
[plain]?view plain?copy
vector_stub?irq,?IRQ_MODE,?4??
以上這一句把宏展開后實際上就是定義了vector_irq,根據(jù)進入中斷前的cpu模式,分別跳轉(zhuǎn)到__irq_usr或__irq_svc。
[plain]?view plain?copy
vector_stub?dabt,?ABT_MODE,?8??
以上這一句把宏展開后實際上就是定義了vector_dabt,根據(jù)進入中斷前的cpu模式,分別跳轉(zhuǎn)到__dabt_usr或__dabt_svc。
系統(tǒng)啟動階段,位于arch/arm/kernel/traps.c中的early_trap_init()被調(diào)用:
[cpp]?view plain?copy
void?__init?early_trap_init(void)??
{??
......??
/*?
*?Copy?the?vectors,?stubs?and?kuser?helpers?(in?entry-armv.S)?
*?into?the?vector?page,?mapped?at?0xffff0000,?and?ensure?these?
*?are?visible?to?the?instruction?stream.?
*/??
memcpy((void?*)vectors,?__vectors_start,?__vectors_end?-?__vectors_start);??
memcpy((void?*)vectors?+?0x200,?__stubs_start,?__stubs_end?-?__stubs_start);??
......??
}??
以上兩個memcpy會把__vectors_start開始的代碼拷貝到0xffff0000處,把__stubs_start開始的代碼拷貝到0xFFFF0000+0x200處,這樣,異常中斷到來時,CPU就可以正確地跳轉(zhuǎn)到相應(yīng)中斷向量入口并執(zhí)行他們。
圖1.1 ?Linux中ARM體系的中斷向量拷貝過程
對于系統(tǒng)的外部設(shè)備來說,通常都是使用IRQ中斷,所以我們只關(guān)注__irq_usr和__irq_svc,兩者的區(qū)別是進入和退出中斷時是否進行用戶棧和內(nèi)核棧之間的切換,還有進程調(diào)度和搶占的處理等,這些細節(jié)不在這里討論。兩個函數(shù)最終都會進入irq_handler這個宏:
[plain]?view plain?copy
.macro??irq_handler??
#ifdef?CONFIG_MULTI_IRQ_HANDLER??
ldr?r1,?=handle_arch_irq??
mov?r0,?sp??
adr?lr,?BSYM(9997f)??
ldr?pc,?[r1]??
#else??
arch_irq_handler_default??
#endif??
9997:??
.endm??
如果選擇了MULTI_IRQ_HANDLER配置項,則意味著允許平臺的代碼可以動態(tài)設(shè)置irq處理程序,平臺代碼可以修改全局變量:handle_arch_irq,從而可以修改irq的處理程序。這里我們討論默認(rèn)的實現(xiàn):arch_irq_handler_default,它位于arch/arm/include/asm/entry_macro_multi.S中:
[plain]?view plain?copy
.macro??arch_irq_handler_default??
get_irqnr_preamble?r6,?lr??
1:??get_irqnr_and_base?r0,?r2,?r6,?lr??
movne???r1,?sp??
@??
@?routine?called?with?r0?=?irq?number,?r1?=?struct?pt_regs?*??
@??
adrne???lr,?BSYM(1b)??
bne?asm_do_IRQ??
......??
get_irqnr_preamble和get_irqnr_and_base兩個宏由machine級的代碼定義,目的就是從中斷控制器中獲得IRQ編號,緊接著就調(diào)用asm_do_IRQ,從這個函數(shù)開始,中斷程序進入C代碼中,傳入的參數(shù)是IRQ編號和寄存器結(jié)構(gòu)指針,這個函數(shù)在arch/arm/kernel/irq.c中實現(xiàn):
[cpp]?view plain?copy
/*?
*?asm_do_IRQ?is?the?interface?to?be?used?from?assembly?code.?
*/??
asmlinkage?void?__exception_irq_entry??
asm_do_IRQ(unsigned?int?irq,?struct?pt_regs?*regs)??
{??
handle_IRQ(irq,?regs);??
}??
到這里,中斷程序完成了從asm代碼到C代碼的傳遞,并且獲得了引起中斷的IRQ編號。
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é)所說的中斷向量的拷貝和重定位工作。
然后,start_kernel發(fā)出early_irq_init調(diào)用,early_irq_init屬于與硬件和平臺無關(guān)的通用邏輯層,它完成irq_desc結(jié)構(gòu)的內(nèi)存申請,為它們其中某些字段填充默認(rèn)值,完成后調(diào)用體系相關(guān)的arch_early_irq_init函數(shù)完成進一步的初始化工作,不過ARM體系沒有實現(xiàn)arch_early_irq_init。
接著,start_kernel發(fā)出init_IRQ調(diào)用,它會直接調(diào)用所屬板子machine_desc結(jié)構(gòu)體中的init_irq回調(diào)。machine_desc通常在板子的特定代碼中,使用MACHINE_START和MACHINE_END宏進行定義。
machine_desc->init_irq()完成對中斷控制器的初始化,為每個irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對應(yīng)的irq_chip結(jié)構(gòu)的實例,同時,如果該平臺中的中斷線有多路復(fù)用(多個中斷公用一個irq中斷線)的情況,還應(yīng)該初始化irq_desc中相應(yīng)的字段和標(biāo)志,以便實現(xiàn)中斷控制器的級聯(lián)。
3.? 中斷控制器的軟件抽象:struct irq_chip
正如上一篇文章Linux中斷(interrupt)子系統(tǒng)之一:中斷系統(tǒng)基本原理所述,所有的硬件中斷在到達CPU之前,都要先經(jīng)過中斷控制器進行匯集,合乎要求的中斷請求才會通知cpu進行處理,中斷控制器主要完成以下這些功能:
對各個irq的優(yōu)先級進行控制;
向CPU發(fā)出中斷請求后,提供某種機制讓CPU獲得實際的中斷源(irq編號);
控制各個irq的電氣觸發(fā)條件,例如邊緣觸發(fā)或者是電平觸發(fā);
使能(enable)或者屏蔽(mask)某一個irq;
提供嵌套中斷請求的能力;
提供清除中斷請求的機制(ack);
有些控制器還需要CPU在處理完irq后對控制器發(fā)出eoi指令(end of interrupt);
在smp系統(tǒng)中,控制各個irq與cpu之間的親緣關(guān)系(affinity);
通用中斷子系統(tǒng)把中斷控制器抽象為一個數(shù)據(jù)結(jié)構(gòu):struct irq_chip,其中定義了一系列的操作函數(shù),大部分多對應(yīng)于上面所列的某個功能:
[cpp]?view plain?copy
struct?irq_chip?{??
const?char??*name;??
unsigned?int????(*irq_startup)(struct?irq_data?*data);??
void????????(*irq_shutdown)(struct?irq_data?*data);??
void????????(*irq_enable)(struct?irq_data?*data);??
void????????(*irq_disable)(struct?irq_data?*data);??
void????????(*irq_ack)(struct?irq_data?*data);??
void????????(*irq_mask)(struct?irq_data?*data);??
void????????(*irq_mask_ack)(struct?irq_data?*data);??
void????????(*irq_unmask)(struct?irq_data?*data);??
void????????(*irq_eoi)(struct?irq_data?*data);??
int?????(*irq_set_affinity)(struct?irq_data?*data,?const?struct?cpumask?*dest,?bool?force);??
int?????(*irq_retrigger)(struct?irq_data?*data);??
int?????(*irq_set_type)(struct?irq_data?*data,?unsigned?int?flow_type);??
int?????(*irq_set_wake)(struct?irq_data?*data,?unsigned?int?on);??
void????????(*irq_bus_lock)(struct?irq_data?*data);??
void????????(*irq_bus_sync_unlock)(struct?irq_data?*data);??
void????????(*irq_cpu_online)(struct?irq_data?*data);??
void????????(*irq_cpu_offline)(struct?irq_data?*data);??
void????????(*irq_suspend)(struct?irq_data?*data);??
void????????(*irq_resume)(struct?irq_data?*data);??
void????????(*irq_pm_shutdown)(struct?irq_data?*data);??
void????????(*irq_print_chip)(struct?irq_data?*data,?struct?seq_file?*p);??
unsigned?long???flags;??
/*?Currently?used?only?by?UML,?might?disappear?one?day.*/??
#ifdef?CONFIG_IRQ_RELEASE_METHOD??
void????????(*release)(unsigned?int?irq,?void?*dev_id);??
#endif??
};??
各個字段解釋如下:
name??中斷控制器的名字,會出現(xiàn)在 /proc/interrupts中。
irq_startup? 第一次開啟一個irq時使用。
irq_shutdown? 與irq_starup相對應(yīng)。
irq_enable? 使能該irq,通常是直接調(diào)用irq_unmask()。
irq_disable? 禁止該irq,通常是直接調(diào)用irq_mask,嚴(yán)格意義上,他倆其實代表不同的意義,disable表示中斷控制器根本就不響應(yīng)該irq,而mask時,中斷控制器可能響應(yīng)該irq,只是不通知CPU,這時,該irq處于pending狀態(tài)。類似的區(qū)別也適用于enable和unmask。
irq_ack? 用于CPU對該irq的回應(yīng),通常表示cpu希望要清除該irq的pending狀態(tài),準(zhǔn)備接受下一個irq請求。
irq_mask? 屏蔽該irq。
irq_unmask? 取消屏蔽該irq。
irq_mask_ack? 相當(dāng)于irq_mask + irq_ack。
irq_eoi? 有些中斷控制器需要在cpu處理完該irq后發(fā)出eoi信號,該回調(diào)就是用于這個目的。
irq_set_affinity? 用于設(shè)置該irq和cpu之間的親緣關(guān)系,就是通知中斷控制器,該irq發(fā)生時,那些cpu有權(quán)響應(yīng)該irq。當(dāng)然,中斷控制器會在軟件的配合下,最終只會讓一個cpu處理本次請求。
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)的由來在上一篇文章已經(jīng)說過,這里僅貼出它的定義,各字段的意義請參考注釋:
[cpp]?view plain?copy
/**?
*?struct?irq_data?-?per?irq?and?irq?chip?data?passed?down?to?chip?functions?
*?@irq:????????interrupt?number?
*?@hwirq:??????hardware?interrupt?number,?local?to?the?interrupt?domain?
*?@node:???????node?index?useful?for?balancing?
*?@state_use_accessors:?status?information?for?irq?chip?functions.?
*??????????Use?accessor?functions?to?deal?with?it?
*?@chip:???????low?level?interrupt?hardware?access?
*?@domain:?????Interrupt?translation?domain;?responsible?for?mapping?
*??????????between?hwirq?number?and?linux?irq?number.?
*?@handler_data:???per-IRQ?data?for?the?irq_chip?methods?
*?@chip_data:??????platform-specific?per-chip?private?data?for?the?chip?
*??????????methods,?to?allow?shared?chip?implementations?
*?@msi_desc:???????MSI?descriptor?
*?@affinity:???????IRQ?affinity?on?SMP?
*?
*?The?fields?here?need?to?overlay?the?ones?in?irq_desc?until?we?
*?cleaned?up?the?direct?references?and?switched?everything?over?to?
*?irq_data.?
*/??
struct?irq_data?{??
unsigned?int????????irq;??
unsigned?long???????hwirq;??
unsigned?int????????node;??
unsigned?int????????state_use_accessors;??
struct?irq_chip?????*chip;??
struct?irq_domain???*domain;??
void????????????*handler_data;??
void????????????*chip_data;??
struct?msi_desc?????*msi_desc;??
#ifdef?CONFIG_SMP??
cpumask_var_t???????affinity;??
#endif??
};??
根據(jù)設(shè)備使用的中斷控制器的類型,體系架構(gòu)的底層的開發(fā)只要實現(xiàn)上述接口中的各個回調(diào)函數(shù),然后把它們填充到irq_chip結(jié)構(gòu)的實例中,最終把該irq_chip實例注冊到irq_desc.irq_data.chip字段中,這樣各個irq和中斷控制器就進行了關(guān)聯(lián),只要知道irq編號,即可得到對應(yīng)到irq_desc結(jié)構(gòu),進而可以通過chip指針訪問中斷控制器。?
4.? 進入流控處理層
進入C代碼的第一個函數(shù)是asm_do_IRQ,在ARM體系中,這個函數(shù)只是簡單地調(diào)用handle_IRQ:
[cpp]?view plain?copy
/*?
*?asm_do_IRQ?is?the?interface?to?be?used?from?assembly?code.?
*/??
asmlinkage?void?__exception_irq_entry??
asm_do_IRQ(unsigned?int?irq,?struct?pt_regs?*regs)??
{??
handle_IRQ(irq,?regs);??
}??
handle_IRQ本身也不是很復(fù)雜:
[cpp]?view plain?copy
void?handle_IRQ(unsigned?int?irq,?struct?pt_regs?*regs)??
{??
struct?pt_regs?*old_regs?=?set_irq_regs(regs);??
irq_enter();??
/*?
*?Some?hardware?gives?randomly?wrong?interrupts.??Rather?
*?than?crashing,?do?something?sensible.?
*/??
if?(unlikely(irq?>=?nr_irqs))?{??
if?(printk_ratelimit())??
printk(KERN_WARNING?"Bad?IRQ%u ",?irq);??
ack_bad_irq(irq);??
}?else?{??
generic_handle_irq(irq);??
}??
/*?AT91?specific?workaround?*/??
irq_finish(irq);??
irq_exit();??
set_irq_regs(old_regs);??
}??
irq_enter主要是更新一些系統(tǒng)的統(tǒng)計信息,同時在__irq_enter宏中禁止了進程的搶占:
[cpp]?view plain?copy
#define?__irq_enter()?????????????????????
do?{??????????????????????????
account_system_vtime(current);????????
add_preempt_count(HARDIRQ_OFFSET);????
trace_hardirq_enter();????????????
}?while?(0)??
CPU一旦響應(yīng)IRQ中斷后,ARM會自動把CPSR中的I位置位,表明禁止新的IRQ請求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過local_irq_enable()打開。你可能會奇怪,既然此時的irq中斷都是都是被禁止的,為何還要禁止搶占?這是因為要考慮中斷嵌套的問題,一旦流控層或驅(qū)動程序主動通過local_irq_enable打開了IRQ,而此時該中斷還沒處理完成,新的irq請求到達,這時代碼會再次進入irq_enter,在本次嵌套中斷返回時,內(nèi)核不希望進行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動作,所以才有了禁止搶占這一處理。
下一步,generic_handle_irq被調(diào)用,generic_handle_irq是通用邏輯層提供的API,通過該API,中斷的控制被傳遞到了與體系結(jié)構(gòu)無關(guān)的中斷流控層:
[cpp]?view plain?copy
int?generic_handle_irq(unsigned?int?irq)??
{??
struct?irq_desc?*desc?=?irq_to_desc(irq);??
if?(!desc)??
return?-EINVAL;??
generic_handle_irq_desc(irq,?desc);??
return?0;??
}??
最終會進入該irq注冊的流控處理回調(diào)中:
[cpp]?view plain?copy
static?inline?void?generic_handle_irq_desc(unsigned?int?irq,?struct?irq_desc?*desc)??
{??
desc->handle_irq(irq,?desc);??
}??
5. ?中斷控制器的級聯(lián)
在實際的設(shè)備中,經(jīng)常存在多個中斷控制器,有時多個中斷控制器還會進行所謂的級聯(lián)。為了方便討論,我們把直接和CPU相連的中斷控制器叫做根控制器,另外一些和跟控制器相連的叫子控制器。根據(jù)子控制器的位置,我們把它們分為兩種類型:
機器級別的級聯(lián) ?子控制器位于SOC內(nèi)部,或者子控制器在SOC的外部,但是是某個板子系列的標(biāo)準(zhǔn)配置,如圖5.1的左邊所示;
設(shè)備級別的級聯(lián) ?子控制器位于某個外部設(shè)備中,用于匯集該設(shè)備發(fā)出的多個中斷,如圖5.1的右邊所示;
圖5.1 ?中斷控制器的級聯(lián)類型
對于機器級別的級聯(lián),級聯(lián)的初始化代碼理所當(dāng)然地位于板子的初始化代碼中(arch/xxx/mach-xxx),因為只要是使用這個板子或SOC的設(shè)備,必然要使用這個子控制器。而對于設(shè)備級別的級聯(lián),因為該設(shè)備并不一定是系統(tǒng)的標(biāo)配設(shè)備,所以中斷控制器的級聯(lián)操作應(yīng)該在該設(shè)備的驅(qū)動程序中實現(xiàn)。機器設(shè)備的級聯(lián),因為得益于事先已經(jīng)知道子控制器的硬件連接信息,內(nèi)核可以方便地為子控制器保留相應(yīng)的irq_desc結(jié)構(gòu)和irq編號,處理起來相對簡單。設(shè)備級別的級聯(lián)則不一樣,驅(qū)動程序必須動態(tài)地決定組合設(shè)備中各個子設(shè)備的irq編號和irq_desc結(jié)構(gòu)。本章我只討論機器級別的級聯(lián),設(shè)備級別的關(guān)聯(lián)可以使用同樣的原理,也可以實現(xiàn)為共享中斷,我會在本系列接下來的文章中討論。
要實現(xiàn)中斷控制器的級聯(lián),要使用以下幾個的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)字段和通用中斷邏輯層的API:
irq_desc.handle_irq? irq的流控處理回調(diào)函數(shù),子控制器在把多個irq匯集起來后,輸出端連接到根控制器的其中一個irq中斷線輸入腳,這意味著,每個子控制器的中斷發(fā)生時,CPU一開始只會得到根控制器的irq編號,然后進入該irq編號對應(yīng)的irq_desc.handle_irq回調(diào),該回調(diào)我們不能使用流控層定義好的幾個流控函數(shù),而是要自己實現(xiàn)一個函數(shù),該函數(shù)負責(zé)從子控制器中獲得irq的中斷源,并計算出對應(yīng)新的irq編號,然后調(diào)用新irq所對應(yīng)的irq_desc.handle_irq回調(diào),這個回調(diào)使用流控層的標(biāo)準(zhǔn)實現(xiàn)。
irq_set_chained_handler()? 該API用于設(shè)置根控制器與子控制器相連的irq所對應(yīng)的irq_desc.handle_irq回調(diào)函數(shù),并且設(shè)置IRQ_NOPROBE和IRQ_NOTHREAD以及IRQ_NOREQUEST標(biāo)志,這幾個標(biāo)志保證驅(qū)動程序不會錯誤地申請該irq,因為該irq已經(jīng)被作為級聯(lián)irq使用。
irq_set_chip_and_handler()? 該API同時設(shè)置irq_desc中的handle_irq回調(diào)和irq_chip指針。
以下例子代碼位于:/arch/arm/plat-s5p/irq-eint.c:
[cpp]?view plain?copy
int?__init?s5p_init_irq_eint(void)??
{??
int?irq;??
for?(irq?=?IRQ_EINT(0);?irq?<=?IRQ_EINT(15);?irq++)??
irq_set_chip(irq,?&s5p_irq_vic_eint);??
for?(irq?=?IRQ_EINT(16);?irq?<=?IRQ_EINT(31);?irq++)?{??
irq_set_chip_and_handler(irq,?&s5p_irq_eint,?handle_level_irq);??
set_irq_flags(irq,?IRQF_VALID);??
}??
irq_set_chained_handler(IRQ_EINT16_31,?s5p_irq_demux_eint16_31);??
return?0;??
}??
該SOC芯片的外部中斷:IRQ_EINT(0)到IRQ_EINT(15),每個引腳對應(yīng)一個根控制器的irq中斷線,它們是正常的irq,無需級聯(lián)。IRQ_EINT(16)到IRQ_EINT(31)經(jīng)過子控制器匯集后,統(tǒng)一連接到根控制器編號為IRQ_EINT16_31這個中斷線上??梢钥吹?,子控制器對應(yīng)的irq_chip是s5p_irq_eint,子控制器的irq默認(rèn)設(shè)置為電平中斷的流控處理函數(shù)handle_level_irq,它們通過API:irq_set_chained_handler進行設(shè)置。如果根控制器有128個中斷線,IRQ_EINT0--IRQ_EINT15通常占據(jù)128內(nèi)的某段連續(xù)范圍,這取決于實際的物理連接。IRQ_EINT16_31因為也屬于跟控制器,所以它的值也會位于128以內(nèi),但是IRQ_EINT16--IRQ_EINT31通常會在128以外的某段范圍,這時,代表irq數(shù)量的常量NR_IRQS,必須考慮這種情況,定義出超過128的某個足夠的數(shù)值。級聯(lián)的實現(xiàn)主要依靠編號為IRQ_EINT16_31的流控處理程序:s5p_irq_demux_eint16_31,它的最終實現(xiàn)類似于以下代碼:
[cpp]?view plain?copy
static?inline?void?s5p_irq_demux_eint(unsigned?int?start)??
{??
u32?status?=?__raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));??
u32?mask?=?__raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));??
unsigned?int?irq;??
status?&=?~mask;??
status?&=?0xff;??
while?(status)?{??
irq?=?fls(status)?-?1;??
generic_handle_irq(irq?+?start);??
status?&=?~(1?<
}??
}??
在獲得新的irq編號后,它的最關(guān)鍵的一句是調(diào)用了通用中斷邏輯層的API:generic_handle_irq,這時它才真正地把中斷控制權(quán)傳遞到中斷流控層中來。
?
評論
查看更多