linux kernel的中斷子系統(tǒng)之(四):High level irq event handler作者:linuxer 發(fā)布于:2014-8-28 20:00 分類(lèi):中斷子系統(tǒng) 一、前言 當(dāng)外設(shè)觸發(fā)一次中斷后,一個(gè)大概的處理過(guò)程是: 1、具體CPU architecture相關(guān)的模塊會(huì)進(jìn)行現(xiàn)場(chǎng)保護(hù),然后調(diào)用machine driver對(duì)應(yīng)的中斷處理handler 2、machine driver對(duì)應(yīng)的中斷處理handler中會(huì)根據(jù)硬件的信息獲取HW interrupt ID,并且通過(guò)irq domain模塊翻譯成IRQ number 3、調(diào)用該IRQ number對(duì)應(yīng)的high level irq event handler,在這個(gè)high level的handler中,會(huì)通過(guò)和interupt controller交互,進(jìn)行中斷處理的flow control(處理中斷的嵌套、搶占等),當(dāng)然最終會(huì)遍歷該中斷描述符的IRQ action list,調(diào)用外設(shè)的specific handler來(lái)處理該中斷 4、具體CPU architecture相關(guān)的模塊會(huì)進(jìn)行現(xiàn)場(chǎng)恢復(fù)。 上面的1、4這兩個(gè)步驟在linux kernel的中斷子系統(tǒng)之(六):ARM中斷處理過(guò)程中已經(jīng)有了較為細(xì)致的描述,步驟2在linux kernel的中斷子系統(tǒng)之(二):irq domain介紹中介紹,本文主要描述步驟3,也就是linux中斷子系統(tǒng)的high level irq event handler。
注:這份文檔充滿(mǎn)了猜測(cè)和空想,很多地方描述可能是有問(wèn)題的,不過(guò)我還是把它發(fā)出來(lái),拋磚引玉,希望可以引發(fā)大家討論。
一、如何進(jìn)入high level irq event handler 1、從具體CPU architecture的中斷處理到machine相關(guān)的處理模塊 說(shuō)到具體的CPU,我們還是用ARM為例好了。對(duì)于ARM,我們?cè)?a href="http://www./linux_kenrel/irq_handler.html">ARM中斷處理文檔中已經(jīng)有了較為細(xì)致的描述。這里我們看看如何從從具體CPU的中斷處理到machine相關(guān)的處理模塊 ,其具體代碼如下:
其實(shí),直接從CPU的中斷處理跳轉(zhuǎn)到通用中斷處理模塊是不可能的,中斷處理不可能越過(guò)interrupt controller這個(gè)層次。一般而言,通用中斷處理模塊會(huì)提供一些通用的中斷代碼處理庫(kù),然后由interrupt controller這個(gè)層次的代碼調(diào)用這些通用中斷處理的完成整個(gè)的中斷處理過(guò)程。“interrupt controller這個(gè)層次的代碼”是和硬件中斷系統(tǒng)設(shè)計(jì)相關(guān)的,例如:系統(tǒng)中有多少個(gè)interrupt contrller,每個(gè)interrupt controller是如何控制的?它們是如何級(jí)聯(lián)的?我們稱(chēng)這些相關(guān)的驅(qū)動(dòng)模塊為machine interrupt driver。 在上面的代碼中,如果配置了MULTI_IRQ_HANDLER的話(huà),ARM中斷處理則直接跳轉(zhuǎn)到一個(gè)叫做handle_arch_irq函數(shù),如果系統(tǒng)中只有一個(gè)類(lèi)型的interrupt controller(可能是多個(gè)interrupt controller,例如使用兩個(gè)級(jí)聯(lián)的GIC),那么handle_arch_irq可以在interrupt controller初始化的時(shí)候設(shè)定。代碼如下:
gic_nr是GIC的編號(hào),linux kernel初始化過(guò)程中,每發(fā)現(xiàn)一個(gè)GIC,都是會(huì)指向GIC driver的初始化函數(shù)的,不過(guò)對(duì)于第一個(gè)GIC,gic_nr等于0,對(duì)于第二個(gè)GIC,gic_nr等于1。當(dāng)然handle_arch_irq這個(gè)函數(shù)指針不是per CPU的變量,是全部CPU共享的,因此,初始化一次就OK了。 當(dāng)使用多種類(lèi)型的interrupt controller的時(shí)候(例如HW 系統(tǒng)使用了S3C2451這樣的SOC,這時(shí)候,系統(tǒng)有兩種interrupt controller,一種是GPIO type,另外一種是SOC上的interrupt controller),則不適合在interrupt controller中進(jìn)行設(shè)定,這時(shí)候,可以考慮在machine driver中設(shè)定。在這種情況下,handle_arch_irq 這個(gè)函數(shù)是在setup_arch函數(shù)中根據(jù)machine driver設(shè)定,具體如下:
關(guān)于MULTI_IRQ_HANDLER這個(gè)配置項(xiàng),我們可以再多說(shuō)幾句。當(dāng)然,其實(shí)這個(gè)配置項(xiàng)的名字已經(jīng)出賣(mài)它了。multi irq handler就是說(shuō)系統(tǒng)中有多個(gè)irq handler,可以在run time的時(shí)候指定。為何要run time的時(shí)候,從多個(gè)handler中選擇一個(gè)呢?HW interrupt block難道不是固定的嗎?我的理解(猜想)是:一個(gè)kernel的image支持多個(gè)HW platform,對(duì)于不同的HW platform,在運(yùn)行時(shí)檢查HW platform的類(lèi)型,設(shè)定不同的irq handler。 2、interrupt controller相關(guān)的代碼 我們還是以2個(gè)級(jí)聯(lián)的GIC為例來(lái)描述interrupt controller相關(guān)的代碼。代碼如下:
更多關(guān)于GIC相關(guān)的信息,請(qǐng)參考linux kernel的中斷子系統(tǒng)之(七):GIC代碼分析。對(duì)于ARM處理器,handle_IRQ代碼如下:
3、調(diào)用high level handler 調(diào)用high level handler的代碼邏輯非常簡(jiǎn)單,如下:
二、理解high level irq event handler需要的知識(shí)準(zhǔn)備 1、自動(dòng)探測(cè)IRQ 一個(gè)硬件驅(qū)動(dòng)可以通過(guò)下面的方法進(jìn)行自動(dòng)探測(cè)它使用的IRQ:
如果能夠自動(dòng)探測(cè)到IRQ,上面程序中的irq(probe_irq_off的返回值)就是自動(dòng)探測(cè)的結(jié)果。后續(xù)程序可以通過(guò)request_threaded_irq申請(qǐng)?jiān)揑RQ。probe_irq_on函數(shù)主要的目的是返回一個(gè)32 bit的掩碼,通過(guò)該掩碼可以知道可能使用的IRQ number有哪些,具體代碼如下:
(1)那些能自動(dòng)探測(cè)IRQ的中斷描述符需要具體兩個(gè)條件: a、該中斷描述符還沒(méi)有通過(guò)request_threaded_irq或者其他方式申請(qǐng)?jiān)揑RQ的specific handler(也就是irqaction數(shù)據(jù)結(jié)構(gòu)) b、該中斷描述符允許自動(dòng)探測(cè)(不能設(shè)定IRQ_NOPROBE) (2)如果滿(mǎn)足上面的條件,那么該中斷描述符屬于備選描述符。設(shè)定其internal state為IRQS_AUTODETECT | IRQS_WAITING。IRQS_AUTODETECT表示本IRQ正處于自動(dòng)探測(cè)中。 (3)在等待過(guò)程中,系統(tǒng)仍然允許,各種中斷依然會(huì)觸發(fā)。在各種high level irq event handler中,總會(huì)有如下的代碼:
這里會(huì)清除IRQS_WAITING狀態(tài)。 (4)這時(shí)候,我們還沒(méi)有控制那個(gè)想要自動(dòng)探測(cè)IRQ的硬件產(chǎn)生中斷,因此處于自動(dòng)探測(cè)中,并且IRQS_WAITING并清除的一定不是我們期待的IRQ(可能是spurious interrupts導(dǎo)致的),這時(shí)候,clear IRQS_AUTODETECT,shutdown該IRQ。 (5)最大探測(cè)的IRQ是31(mask是一個(gè)32 bit的value),mask返回的是可能的irq掩碼。 我們?cè)賮?lái)看看probe_irq_off的代碼:
因?yàn)樵谡{(diào)用probe_irq_off已經(jīng)觸發(fā)了自動(dòng)探測(cè)IRQ的那個(gè)硬件中斷,因此在該中斷的high level handler的執(zhí)行過(guò)程中,該硬件對(duì)應(yīng)的中斷描述符的IRQS_WAITING標(biāo)致應(yīng)該已經(jīng)被清除,因此probe_irq_off函數(shù)scan中斷描述符DB,找到處于auto probe中,而且IRQS_WAITING標(biāo)致被清除的那個(gè)IRQ。如果找到一個(gè),那么探測(cè)OK,返回該IRQ number,如果找到多個(gè),說(shuō)明探測(cè)失敗,返回負(fù)的IRQ個(gè)數(shù)信息,沒(méi)有找到的話(huà),返回0。
一個(gè)ARM SOC總是有很多的GPIO,有些GPIO可以提供中斷功能,這些GPIO的中斷可以配置成level trigger或者edge trigger。一般而言,大家都更喜歡用level trigger的中斷。有的SOC只能是有限個(gè)數(shù)的GPIO可以配置成電平中斷,因此,在項(xiàng)目初期進(jìn)行pin define的時(shí)候,大家都在爭(zhēng)搶電平觸發(fā)的GPIO。 電平觸發(fā)的中斷有什么好處呢?電平觸發(fā)的中斷很簡(jiǎn)單、直接,只要硬件檢測(cè)到硬件事件(例如有數(shù)據(jù)到來(lái)),其assert指定的電平信號(hào),CPU ack該中斷后,電平信號(hào)消失。但是對(duì)于邊緣觸發(fā)的中斷,它是用一個(gè)上升沿或者下降沿告知硬件的狀態(tài),這個(gè)狀態(tài)不是一個(gè)持續(xù)的狀態(tài),如果軟件處理不好,容易丟失中斷。 什么時(shí)候會(huì)resend一個(gè)中斷呢?我們考慮一個(gè)簡(jiǎn)單的例子: (1)CPU A上正在處理x外設(shè)的中斷 (2)x外設(shè)的中斷再次到來(lái)(CPU A已經(jīng)ack該IRQ,因此x外設(shè)的中斷可以再次觸發(fā)),這時(shí)候其他CPU會(huì)處理它(mask and ack),并設(shè)置該中斷描述符是pending狀態(tài),并委托CPU A處理該pending狀態(tài)的中斷。需要注意的是CPU已經(jīng)ack了該中斷,因此該中斷的硬件狀態(tài)已經(jīng)不是pending狀態(tài),無(wú)法觸發(fā)中斷了,這里的pending狀態(tài)是指中斷描述符的軟件狀態(tài)。 (3)CPU B上由于同步的需求,disable了x外設(shè)的IRQ,這時(shí)候,CPU A沒(méi)有處理pending狀態(tài)的x外設(shè)中斷就離開(kāi)了中斷處理過(guò)程。 (4)當(dāng)enable x外設(shè)的IRQ的時(shí)候,需要檢測(cè)pending狀態(tài)以便resend該中斷,否則,該中斷會(huì)丟失的 具體代碼如下:
在各種high level irq event handler中,總會(huì)有如下的代碼:
這里會(huì)清除IRQS_REPLAY狀態(tài),表示該中斷已經(jīng)被retrigger,一次resend interrupt的過(guò)程結(jié)束。
3、unhandled interrupt和spurious interrupt 在中斷處理的最后,總會(huì)有一段代碼如下:
note_interrupt就是進(jìn)行unhandled interrupt和spurious interrupt處理的。對(duì)于這類(lèi)中斷,linux kernel有一套復(fù)雜的機(jī)制來(lái)處理,你可以通過(guò)command line參數(shù)(noirqdebug)來(lái)控制開(kāi)關(guān)該功能。 當(dāng)發(fā)生了一個(gè)中斷,但是沒(méi)有被處理(有兩種可能,一種是根本沒(méi)有注冊(cè)的specific handler,第二種是有handler,但是handler否認(rèn)是自己對(duì)應(yīng)的設(shè)備觸發(fā)的中斷),怎么辦?毫無(wú)疑問(wèn)這是一個(gè)異常狀況,那么kernel是否要立刻采取措施將該IRQ disable呢?也不太合適,畢竟interrupt request信號(hào)線(xiàn)是允許共享的,直接disable該IRQ有可能會(huì)下手太狠,kernel采取了這樣的策略:如果該IRQ觸發(fā)了100,000次,但是99,900次沒(méi)有處理,在這種條件下,我們就是disable這個(gè)interrupt request line。多么有情有義的策略?。∠嚓P(guān)的控制數(shù)據(jù)在中斷描述符中,如下:
irq_count和irqs_unhandled都是比較直觀的,為何要記錄unhandled interrupt發(fā)生的時(shí)間呢?我們來(lái)看具體的代碼。具體的相關(guān)代碼位于note_interrupt中,如下:
(1)是否是一次有效的unhandled interrupt還要根據(jù)時(shí)間來(lái)判斷。一般而言,當(dāng)硬件處于異常狀態(tài)的時(shí)候往往是非常短的時(shí)間觸發(fā)非常多次的中斷,如果距離上次unhandled interrupt的時(shí)間超過(guò)了10秒(HZ=100),那么我們要把irqs_unhandled重新計(jì)數(shù)。如果不這么處理的話(huà),隨著時(shí)間的累計(jì),最終irqs_unhandled可能會(huì)達(dá)到99900次的,從而把這個(gè)IRQ錯(cuò)誤的推上了審判臺(tái)。 (2)irq_count每次都會(huì)加一,記錄IRQ被觸發(fā)的次數(shù)。但只要大于100000才啟動(dòng) step (3)中的檢查。一旦啟動(dòng)檢查,irq_count會(huì)清零,irqs_unhandled也會(huì)清零,進(jìn)入下一個(gè)檢查周期。 (3)如果滿(mǎn)足條件(IRQ觸發(fā)了100,000次,但是99,900次沒(méi)有處理),disable該IRQ。 (4)啟動(dòng)timer,輪詢(xún)整個(gè)系統(tǒng)中的handler來(lái)處理這個(gè)中斷(輪詢(xún)啊,絕對(duì)是真愛(ài)啊)。這個(gè)timer的callback函數(shù)定義如下:
三、和high level irq event handler相關(guān)的硬件描述 1、CPU layer和Interrupt controller之間的接口 從邏輯層面上看,CPU和interrupt controller之間的接口包括: (1)觸發(fā)中斷的signal。一般而言,這個(gè)(些)信號(hào)是電平觸發(fā)的。對(duì)于ARM CPU,它是nIRQ和nFIQ信號(hào)線(xiàn),對(duì)于X86,它是INT和NMI信號(hào)線(xiàn),對(duì)于PowerPC,這些信號(hào)線(xiàn)包括MC(machine check)、CRIT(critical interrupt)和NON-CRIT(Non critical interrupt)。對(duì)于linux kernel的中斷子系統(tǒng),我們只使用其中一個(gè)信號(hào)線(xiàn)(例如對(duì)于ARM而言,我們只使用nIRQ這個(gè)信號(hào)線(xiàn))。這樣,從CPU層面看,其邏輯動(dòng)作非常的簡(jiǎn)單,不區(qū)分優(yōu)先級(jí),觸發(fā)中斷的那個(gè)信號(hào)線(xiàn)一旦assert,并且CPU沒(méi)有mask中斷,那么軟件就會(huì)轉(zhuǎn)到一個(gè)異常向量執(zhí)行,完畢后返回現(xiàn)場(chǎng)。 (2)Ack中斷的signal。這個(gè)signal可能是物理上的一個(gè)連接CPU和interrupt controller的銅線(xiàn),也可能不是。對(duì)于X86+8259這樣的結(jié)構(gòu),Ack中斷的signal就是nINTA信號(hào)線(xiàn),對(duì)于ARM+GIC而言,這個(gè)信號(hào)就是總線(xiàn)上的一次訪(fǎng)問(wèn)(讀Interrupt Acknowledge Register寄存器)。CPU ack中斷標(biāo)識(shí)cpu開(kāi)啟啟動(dòng)中斷服務(wù)程序(specific handler)去處理該中斷。對(duì)于X86而言,ack中斷可以讓8259將interrupt vector數(shù)據(jù)送到數(shù)據(jù)總線(xiàn)上,從而讓CPU獲取了足夠的處理該中斷的信息。對(duì)于ARM而言,ack中斷的同時(shí)也就是獲取了發(fā)生中斷的HW interrupt ID,總而言之,ack中斷后,CPU獲取了足夠開(kāi)啟執(zhí)行中斷處理的信息。 (3)結(jié)束中斷(EOI,end of interrupt)的signal。這個(gè)signal用來(lái)標(biāo)識(shí)CPU已經(jīng)完成了對(duì)該中斷的處理(specific handler或者ISR,interrupt serivce routine執(zhí)行完畢)。實(shí)際的物理形態(tài)這里就不描述了,和ack中斷signal是類(lèi)似的。 (4)控制總線(xiàn)和數(shù)據(jù)總線(xiàn)接口。通過(guò)這些接口,CPU可以訪(fǎng)問(wèn)(讀寫(xiě))interrupt controller的寄存器。
2、Interrupt controller和Peripheral device之間的接口 所有的系統(tǒng)中,Interrupt controller和Peripheral device之間的接口都是一個(gè)Interrupt Request信號(hào)線(xiàn)。外設(shè)通過(guò)這個(gè)信號(hào)線(xiàn)上的電平或者邊緣向CPU(實(shí)際上是通過(guò)interrupt controller)申請(qǐng)中斷服務(wù)。
四、幾種典型的high level irq event handler 本章主要介紹幾種典型的high level irq event handler,在進(jìn)行high level irq event handler的設(shè)定的時(shí)候需要注意,不是外設(shè)使用電平觸發(fā)就選用handle_level_irq,選用什么樣的high level irq event handler是和Interrupt controller的行為以及外設(shè)電平觸發(fā)方式?jīng)Q定的。介紹每個(gè)典型的handler之前,我會(huì)簡(jiǎn)單的描述該handler要求的硬件行為,如果該外設(shè)的中斷系統(tǒng)符合這個(gè)硬件行為,那么可以選擇該handler為該中斷的high level irq event handler。 1、邊緣觸發(fā)的handler。 使用handle_edge_irq這個(gè)handler的硬件中斷系統(tǒng)行為如下: 我們以上升沿為例描述邊緣中斷的處理過(guò)程(下降沿的觸發(fā)是類(lèi)似的)。當(dāng)interrupt controller檢測(cè)到了上升沿信號(hào),會(huì)將該上升沿狀態(tài)(pending)鎖存在寄存器中,并通過(guò)中斷的signal向CPU觸發(fā)中斷。需要注意:這時(shí)候,外設(shè)和interrupt controller之間的interrupt request信號(hào)線(xiàn)會(huì)保持高電平,這也就意味著interrupt controller不可能檢測(cè)到新的中斷信號(hào)(本身是高電平,無(wú)法形成上升沿)。這個(gè)高電平信號(hào)會(huì)一直保持到軟件ack該中斷(調(diào)用irq chip的irq_ack callback函數(shù))。ack之后,中斷控制器才有可能繼續(xù)探測(cè)上升沿,觸發(fā)下一次中斷。 ARM+GIC組成的系統(tǒng)不符合這個(gè)類(lèi)型。雖然GIC提供了IAR(Interrupt Acknowledge Register)寄存器來(lái)讓ARM來(lái)ack中斷,但是,在調(diào)用high level handler之前,中斷處理程序需要通過(guò)讀取IAR寄存器獲得HW interrpt ID并轉(zhuǎn)換成IRQ number,因此實(shí)際上,對(duì)于GIC的irq chip,它是無(wú)法提供本場(chǎng)景中的irq_ack函數(shù)的。很多GPIO type的interrupt controller符合上面的條件,它們會(huì)提供pending狀態(tài)寄存器,讀可以獲取pending狀態(tài),而向pending狀態(tài)寄存器寫(xiě)1可以ack該中斷,讓interrupt controller可以繼續(xù)觸發(fā)下一次中斷。 handle_edge_irq代碼如下:
(0) 這時(shí)候,中斷仍然是關(guān)閉的,因此不會(huì)有來(lái)自本CPU的并發(fā),使用raw spin lock就防止其他CPU上對(duì)該IRQ的中斷描述符的訪(fǎng)問(wèn)。針對(duì)該spin lock,我們直觀的感覺(jué)是raw_spin_lock和(7)中的raw_spin_unlock是成對(duì)的,實(shí)際上并不是,handle_irq_event中的代碼是這樣的:
實(shí)際上,由于在handle_irq_event中處理action list的耗時(shí)還是比較長(zhǎng)的,因此處理具體的action list的時(shí)候并沒(méi)有持有中斷描述符的spin lock。在如果那樣的話(huà),其他CPU在對(duì)中斷描述符進(jìn)行操作的時(shí)候需要spin的時(shí)間會(huì)很長(zhǎng)的。 (1)判斷是否需要執(zhí)行下面的action list的處理。這里分成幾種情況: a、該中斷事件已經(jīng)被其他的CPU處理了 b、該中斷被其他的CPU disable了 c、該中斷描述符沒(méi)有注冊(cè)specific handler。這個(gè)比較簡(jiǎn)單,如果沒(méi)有irqaction,根本沒(méi)有必要調(diào)用action list的處理 如果該中斷事件已經(jīng)被其他的CPU處理了,那么我們僅僅是設(shè)定pending狀態(tài)(為了委托正在處理的該中斷的那個(gè)CPU進(jìn)行處理),mask_ack_irq該中斷并退出就OK了,并不做具體的處理。另外正在處理該中斷的CPU會(huì)檢查pending狀態(tài),并進(jìn)行處理的。同樣的,如果該中斷被其他的CPU disable了,本就不應(yīng)該繼續(xù)執(zhí)行該中斷的specific handler,我們也是設(shè)定pending狀態(tài),mask and ack中斷就退出了。當(dāng)其他CPU的代碼離開(kāi)臨界區(qū),enable 該中斷的時(shí)候,軟件會(huì)檢測(cè)pending狀態(tài)并resend該中斷。 這里的irq_check_poll代碼如下:
IRQS_POLL_INPROGRESS標(biāo)識(shí)了該IRQ正在被polling(上一章有描述),如果沒(méi)有被輪詢(xún),那么返回false,進(jìn)行正常的設(shè)定pending標(biāo)記、mask and ack中斷。如果正在被輪詢(xún),那么需要等待poll結(jié)束。 (2)ack該中斷。對(duì)于中斷控制器,一旦被ack,表示該外設(shè)的中斷被enable,硬件上已經(jīng)準(zhǔn)備好觸發(fā)下一次中斷了。再次觸發(fā)的中斷會(huì)被調(diào)度到其他的CPU上?,F(xiàn)在,我們可以再次回到步驟(1)中,為什么這里用mask and ack而不是單純的ack呢?如果單純的ack則意味著后續(xù)中斷還是會(huì)觸發(fā),這時(shí)候怎么處理?在pending+in progress的情況下,我們要怎么處理?記錄pending的次數(shù),有意義嗎?由于中斷是完全異步的,也有可能pending的標(biāo)記可能在另外的CPU上已經(jīng)修改為replay的標(biāo)記,這時(shí)候怎么辦?當(dāng)事情變得復(fù)雜的時(shí)候,那一定是本來(lái)方向就錯(cuò)了,因此,mask and ack就是最好的策略,我已經(jīng)記錄了pending狀態(tài),不再考慮pending嵌套的情況。 (3)在調(diào)用specific handler處理具體的中斷的時(shí)候,由于不持有中斷描述符的spin lock,因此其他CPU上有可能會(huì)注銷(xiāo)其specific handler,因此do while循環(huán)之后,desc->action有可能是NULL,如果是這樣,那么mask irq,然后退出就OK了 (4)如果中斷描述符處于pending狀態(tài),那么一定是其他CPU上又觸發(fā)了該interrupt source的中斷,并設(shè)定了pending狀態(tài),“委托”本CPU進(jìn)行處理,這時(shí)候,需要把之前mask住的中斷進(jìn)行unmask的操作。一旦unmask了該interrupt source,后續(xù)的中斷可以繼續(xù)觸發(fā),由其他的CPU處理(仍然是設(shè)定中斷描述符的pending狀態(tài),委托當(dāng)前正在處理該中斷請(qǐng)求的那個(gè)CPU進(jìn)行處理)。 (5)處理該中斷請(qǐng)求事件
(6)只要有pending標(biāo)記,就說(shuō)明該中斷還在pending狀態(tài),需要繼續(xù)處理。當(dāng)然,如果有其他的CPU disable了該interrupt source,那么本次中斷結(jié)束處理。
2、電平觸發(fā)的handler 使用handle_level_irq這個(gè)handler的硬件中斷系統(tǒng)行為如下: 我們以高電平觸發(fā)為例。當(dāng)interrupt controller檢測(cè)到了高電平信號(hào),并通過(guò)中斷的signal向CPU觸發(fā)中斷。這時(shí)候,對(duì)中斷控制器進(jìn)行ack并不能改變interrupt request signal上的電平狀態(tài),一直要等到執(zhí)行具體的中斷服務(wù)程序(specific handler),對(duì)外設(shè)進(jìn)行ack的時(shí)候,電平信號(hào)才會(huì)恢復(fù)成低電平。在對(duì)外設(shè)ack之前,中斷狀態(tài)一直是pending的,如果沒(méi)有mask中斷,那么中斷控制器就會(huì)assert CPU。 handle_level_irq的代碼如下:
(1)考慮CPU<------>interrupt controller<------>device這樣的連接方式中,我們認(rèn)為high level handler主要是和interrupt controller交互,而specific handler(request_irq注冊(cè)的那個(gè))是和device進(jìn)行交互。Level類(lèi)型的中斷的特點(diǎn)就是只要外設(shè)interrupt request line的電平狀態(tài)是有效狀態(tài),對(duì)于interrupt controller,該外設(shè)的interrupt總是active的。由于外設(shè)檢測(cè)到了事件(比如數(shù)據(jù)到來(lái)了),因此assert了指定的電平信號(hào),這個(gè)電平信號(hào)會(huì)一直保持,直到軟件清除了外設(shè)的狀態(tài)寄存器。但是,high level irq event handler這個(gè)層面只能操作Interrupt controller,不能操作具體外設(shè)的寄存器(那應(yīng)該屬于具體外設(shè)的specific interrupt handler處理內(nèi)容,該handler會(huì)掛入中斷描述符中的IRQ action list)。直到在具體的中斷服務(wù)程序(specific handler中)操作具體外設(shè)的寄存器,才能讓這個(gè)asserted電平信號(hào)消息。 正是因?yàn)閘evel trigger的這個(gè)特點(diǎn),因此,在high level handler中首先mask并ack該IRQ。這一點(diǎn)和邊緣觸發(fā)的high level handler有顯著的不同,在handle_edge_irq中,我們僅僅是ack了中斷,并沒(méi)有mask,因?yàn)檫吘売|發(fā)的中斷稍縱即逝,一旦mask了該中斷,容易造成中斷丟失。而對(duì)于電平中斷,我們不得不mask住該中斷,如果不mask住,只要CPU ack中斷,中斷控制器將持續(xù)的assert CPU中斷(因?yàn)橛行щ娖綘顟B(tài)一直保持)。如果我們mask住該中斷,中斷控制器將不再轉(zhuǎn)發(fā)該interrupt source來(lái)的中斷,因此,所有的CPU都不會(huì)感知到該中斷,直到軟件unmask。這里的ack是針對(duì)interrupt controller的ack,本身ack就是為了clear interrupt controller對(duì)該IRQ的狀態(tài)寄存器,不過(guò)由于外部的電平仍然是有效信號(hào),其實(shí)未必能清除interrupt controller的中斷狀態(tài),不過(guò)這是和中斷控制器硬件實(shí)現(xiàn)相關(guān)的。 (2)對(duì)于電平觸發(fā)的high level handler,我們一開(kāi)始就mask并ack了中斷,因此后續(xù)specific handler因該是串行化執(zhí)行的,為何要判斷in progress標(biāo)記呢?不要忘記spurious interrupt,那里會(huì)直接調(diào)用handler來(lái)處理spurious interrupt。 (3)這里有兩個(gè)場(chǎng)景 a、沒(méi)有注冊(cè)specific handler。如果沒(méi)有注冊(cè)handler,那么保持mask并設(shè)定pending標(biāo)記(這個(gè)pending標(biāo)記有什么作用還沒(méi)有想明白)。 b、該中斷被其他的CPU disable了。如果該中斷被其他的CPU disable了,本就不應(yīng)該繼續(xù)執(zhí)行該中斷的specific handler,我們也是設(shè)定pending狀態(tài),mask and ack中斷就退出了。當(dāng)其他CPU的代碼離開(kāi)臨界區(qū),enable 該中斷的時(shí)候,軟件會(huì)檢測(cè)pending狀態(tài)并resend該中斷。 (4)為何是有條件的unmask該IRQ?正常的話(huà)當(dāng)然是umask就OK了,不過(guò)有些threaded interrupt(這個(gè)概念在下一份文檔中描述)要求是one shot的(首次中斷,specific handler中開(kāi)了一槍?zhuān)瑆akeup了irq handler thread,如果允許中斷嵌套,那么在specific handler會(huì)多次開(kāi)槍?zhuān)@也就不是one shot了,有些IRQ的handler thread要求是one shot,也就是不能嵌套specific handler)。
3、支持EOI的handler TODO 原創(chuàng)文章,轉(zhuǎn)發(fā)請(qǐng)注明出處。蝸窩科技。http://www./linux_kenrel/High_level_irq_event_handler.html 標(biāo)簽: 中斷處理 評(píng)論:
heziq
2015-03-23 10:19 @linuxer:
請(qǐng)教幾個(gè)問(wèn)題: 1.有沒(méi)有同時(shí)支持上升沿和下降沿的中斷控制器?當(dāng)然我覺(jué)得不可能有同時(shí)支持高電平和低電平出發(fā)的中斷控制器。 2.gpio type 中斷,如果是上升沿中斷,是不是要使用下拉電阻或者根本不使用上下拉?如果使用了上拉電阻會(huì)怎么樣? 3.gpio type 中斷,如果是高電平,我覺(jué)得肯定要使用下拉電阻,將電平鉗制在低電平。 3.gpio type 中斷,如果是下降沿,是不是要使用上拉電阻,或者不使用。如果使用了下拉電阻會(huì)怎么樣? 4.gpio type 中斷,如果是低電平,我覺(jué)得肯定要使用上拉電阻,將電平鉗制在高電平。 5.如果gpio中斷內(nèi)部使用了上拉電阻,如果外部在使用上拉電阻,會(huì)出現(xiàn)什么副作用嗎。我覺(jué)得有,兩個(gè)上拉電源之間會(huì)互相影響。 5.如果gpio中斷內(nèi)部使用了下拉電阻,如果外部在使用下拉電阻,會(huì)使pin腳的輸入電阻變低,有可能高電平信號(hào)拉不起來(lái)。 6.如果外設(shè)使用邊沿觸發(fā)。比如上升沿,那么它的高電平維持時(shí)間有要求嗎,由高電平變低電平,是外設(shè)自己拉低的?還是interrupt controler控制? 這都是我在工作中碰到的疑問(wèn)。有些設(shè)置不好,會(huì)造成中斷觸發(fā)失敗,有些會(huì)造成功耗過(guò)大。在這里想和大家交流一下。
linuxer
2015-03-23 23:15 @heziq:我單獨(dú)開(kāi)了一篇文章討論這些問(wèn)題,有興趣的讀者可以到http://www./bbs/pull-up-resistor.html去討論。
RobinHsiang
2015-03-26 17:29 @linuxer:一般所說(shuō)GPIO導(dǎo)致漏電,是什么導(dǎo)致的?是比如GPIO設(shè)置,或者類(lèi)似中斷類(lèi)型GPIO錯(cuò)誤的上下拉導(dǎo)致的嗎?
而漏電的方向是從CPU漏到device還是反過(guò)來(lái)漏?這個(gè)主要看電源域嗎? 一般怎么樣去查證項(xiàng)目中有沒(méi)有GPIO上面的漏電呢?
linuxer
2015-03-28 22:22 @RobinHsiang:多謝你的問(wèn)題,讓我仔細(xì)思考了一番(好的問(wèn)題總是有這樣的特性),因此延遲到今天才回復(fù)。
一般而言,漏電(leakage current)是一個(gè)芯片的DC參數(shù)之一,例如Iozl和Iozh這兩個(gè)參數(shù),分別表示引腳處于高阻(High-Impedance)狀態(tài)時(shí),外加高電平和低電平的電流值。由于是高阻狀態(tài),因此這時(shí)候的leakage current應(yīng)該是很小的(例如1uA)。當(dāng)然,我想你這里說(shuō)的不是這種漏電。 我們調(diào)試嵌入式設(shè)備總是從功能開(kāi)始,然后性能,特別是功耗,例如待機(jī)或者關(guān)機(jī)電流,我們都是希望能夠滿(mǎn)足功能的情況下越小越好。當(dāng)大刀闊斧的針對(duì)各個(gè)HW block的的檢查過(guò)后,最后往往會(huì)糾纏在GPIO狀態(tài)的調(diào)整上,正確的GPIO設(shè)定往往能節(jié)省幾個(gè)毫安的電流(例如如果一個(gè)GPIO的狀態(tài)的錯(cuò)誤設(shè)定可能導(dǎo)致0.1mA的電流,那么精細(xì)的調(diào)整10個(gè)GPIO可以節(jié)省1個(gè)mA) 具體電流是source(用你的描述就是從CPU漏到device)還是sink(從device漏到CPU)是和實(shí)際的電路連接、cpu pin的特性以及外設(shè)芯片引腳的特性相關(guān)。例如CPU的一個(gè)GPIO是tri state,假設(shè)在CPU處于suspend的時(shí)候,將該pin設(shè)定為high-impedance狀態(tài),如果對(duì)端連接的是外設(shè)芯片的enable引腳(低電平有效),為了穩(wěn)定的電路狀態(tài),可能需要連接一個(gè)上拉電阻,確保外設(shè)芯片處于disable狀態(tài),以便節(jié)省功耗。如果CPU處于suspend的時(shí)候,將該pin設(shè)定為低電平,那么上拉電阻上將有一個(gè)不小的電流消耗(假設(shè)上拉到3V,上拉電阻是10k,那么CPU在該GPIO上的sink current大約要0.3mA)。如果調(diào)整輸出成高電平,也不會(huì)有這個(gè)sink current。 具體的電路形形色色,這里無(wú)法每一個(gè)都描述了。
Rocky
2015-03-18 17:23 a、沒(méi)有注冊(cè)specific handler。如果沒(méi)有注冊(cè)handler,那么保持mask并設(shè)定pending標(biāo)記(這個(gè)pending標(biāo)記有什么作用還沒(méi)有想明白)。
@linuxer 應(yīng)該沒(méi)有特別的作用,就是代碼簡(jiǎn)潔吧?!要不是為了mark一下IRQS_PENDING,表明此中斷曾經(jīng)來(lái)過(guò)?? if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {-----(3) if(desc->action)//啰嗦吧?! desc->istate |= IRQS_PENDING; goto out_unlock; } ps:寫(xiě)的真的挺好的,看第二遍就理解很清楚了!
linuxer
2015-03-23 23:21 @Rocky:多謝!有機(jī)會(huì)的話(huà)可以考慮出中斷子系統(tǒng)version 2,這次寫(xiě)的很多文檔有些自己也沒(méi)有搞明白,因此很難讓讀者明白。有些自己明白,寫(xiě)出來(lái)又不是那么明白??傊?,這次的中斷系統(tǒng)的文檔就當(dāng)是愛(ài)因斯坦的第一個(gè)小板凳吧,雖然粗糙,但是有些板凳的形狀,需要后續(xù)不斷的打磨。
linuxer
2014-08-29 19:33 補(bǔ)充兩句,實(shí)際上這份文檔我是寫(xiě)不下去了,我準(zhǔn)備從具體的中斷控制器驅(qū)動(dòng)開(kāi)始,把各種中斷控制器的代碼理清楚的話(huà),在回頭review這份文檔,也許這份文檔也是注定要打上“廢棄”的標(biāo)簽
linuxer
2014-09-01 12:18 @forion:我寫(xiě)的也很累啊,哈哈
這種抽象層的東西就是理解起來(lái)比較困難,因此實(shí)際的系統(tǒng)是千奇百怪的,因此,要要足夠的底層知識(shí)的累積才可以悟到上層邏輯的精妙。
linuxer 2014-09-01 18:21 @linuxer:因此實(shí)際的系統(tǒng)是千奇百怪的--->因?yàn)閷?shí)際的系統(tǒng)是千奇百怪的
------------------------------- 這里沒(méi)有精確的表達(dá)我的意思,本來(lái)我想說(shuō)的是:目前我還不掌握足夠的底層知識(shí),需要積累到一定程度才能領(lǐng)會(huì)上層邏輯的精妙。因此,我準(zhǔn)備先潛心研究一下具體的中斷控制器再回頭看看怎么修改這份文檔。目前考慮先看看ARM+GIC系統(tǒng),X86+Multi APIC系統(tǒng)和PowerPC+MPIC系統(tǒng) |
|