A9處理器:EXYNOS4412
中斷:硬件產生(總是需要硬件電路上的一根中斷線):USB線是有VCC、GND、D+、D-四根線組成,所以是不能產生中斷的;
中斷產生的一般過程:中斷源產生中斷------>中斷控制器(根據中斷優先級)------>ARM處理器
對於4412處理器來說,搞清中斷源和中斷號的對應關系是一個很復雜的工作具體可參考arch/arm/mach-exynos/include/mach/irqs.h頭文件
ARM平台查找中斷號:
1.芯片內部外設:利用設備的名字在irqs.h中查找中斷號;
2.芯片外部外設:設備的中斷連接到4412的GPIO引腳,找到GPIO引腳的GPIO號,然后調用函數gpio_to_irq();
中斷函數的注冊:
1.需要包含頭文件<mach/irqs.h>,如果要找4412內部設備的中斷號,只能看文件<mach/irqs.h>
=======================================================
linux的中斷處理機制
GPIO產生中斷,將中斷給GPIO控制器,GPIO控制器再將中斷交給GIC處理,最后GIC交與cpu做處理:如下圖:
mask:設備上級組織(GIC或者GPIO控制器)一般在中斷到達的過程中屏蔽中斷;同時,CPU也可以通過控制CPSR寄存器來屏蔽來自GIC的所有中斷;
中斷處理機制
當中斷產生后,內核會保護當前進程的執行現場,並進入異常處理,通過異常像量表找到產生中斷的中斷號,並由中斷號,在內核中找到中斷號為100的irq_desc:主要用於描述中斷信息,並執行中斷處理函數(需要用戶有注冊行為)。用戶在處理中斷的過程中,首先要先寫好中斷處理函數,封裝到iqaction並由iqaction想內核進行注冊,從而是handler函數有效;
ps:
1.linux內核在啟動時分配了一個irq_desc的數組,數組中共有NR_IRQS個成員。每個irq_desc中記錄對應中斷的各類信息,比如中斷的處理函數,中斷的發生次數等。irq_desc 結構體定義在<linux/irqdesc.h中>
2.irqaction定義在<linux/interrupt.h>每個irqaction用於封裝一個中斷處理函數。結構體由驅動人員負責分配。irqaction中包含中斷號,中斷處理函數指針,中斷的執行標志,中斷名等。
3.中斷處理函數的返回值為 irq_handler_t類型;定義在<linux/interrupt.h>,如下:
irqreturn_t (*irq_handler_t)(int, void *);
中斷處理函數。由驅動負責實現,記錄在irqaction中。
irqreturn_t只有兩個值,IRQ_NONE/IRQ_HANDLED。如果中斷不是由本設備引起的,則返回IRQ_NONE,否則返回IRQ_HANDLED。
函數參數irq為中斷號,void *為傳遞給中斷處理函數的參數,對應irqaction->dev_id。
中斷處理函數的編寫與注冊
驅動人員在設計中斷處理函數時,要遵循的要求是:
(1)可嵌套不可重入
(2)不能睡眠
(3)如果硬件有中斷的狀態寄存器,軟件要負責清除中斷的標志位。一般來說,如果不清除標志位,設備無法再次產生中斷
(4)中斷處理函數的注冊和注銷
0.前提
需要包含的頭文件
#include <linux/interrupt.h>
#include <mach/irqs.h> //片內外設
#include <linux/gpio.h> //片外外設
#include <mach/gpio.h>
1.確定中斷號
正如剛才如上所述:如果是外部IO中斷,那么我們可以通過GPIO號獲取中斷號:
eg:#define KEY_IRQ gpio_to_irq(gpio號);
2.中斷處理函數編寫
中斷處理函數的格式:
eg:
//參數1:int irq :中斷號
//參數2:void * dev_id :為傳遞給中斷處理函數的參數
//返回值:IRQ_NONE:中斷不是由本設備引起的
IRQ_HANDLED:中斷是由本設備引起的
static irqreturn_t key_service(int irq, void *dev_id)
{
//根據硬件要求完成相應工作
...
return IRQ_HANDLED 或 IRQ_NONE;
}
3.注冊中斷處理函數(必須檢查返回值)
u32 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;//定義中斷的標志:下降沿/上升沿/低電平/高電平觸發中斷
int ret;
ret = request_irq( KEY_IRQ, /* 中斷號 */
key_service, /* 中斷處理函數 */
flags, /* 中斷的標志 */
"xxx", /* 中斷處理函數的名字 */
dev_id);/* dev_id為傳給中斷處理函數的參數,一般會設置為私有結構體的指針,非共享>中斷可以為NULL */
if (ret) {
printk("Cannot register interrupt handler\n");
return -1;
}
4.注銷中斷處理函數
free_irq(irq, dev_id); //參數為中斷號和dev_id。 dev_id一定要和request_irq中的最后一個參數一致
5.屏蔽中斷
1.可以人為關閉(mask)/打開某個中斷:
disable_irq(int irq);
enable_irq(int irq);
這兩個函數是可以嵌套的,也就是說,如果對一個中斷disable了3次,需要調用enable函數3次才能打開對該中斷的屏蔽;
2.還可以屏蔽本cpu的中斷:
local_irq_disable();
local_irq_enable();