1. 中斷的概念
中斷是指在CPU正常運(yùn)行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的 CPU 暫時(shí)停止正在運(yùn)行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運(yùn)行被暫時(shí)中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內(nèi)部中斷(又叫異常)。
軟件對(duì)硬件進(jìn)行配置后,軟件期望等待硬件的某種狀態(tài)(比如,收到了數(shù)據(jù)),這里有兩種方式,一種是輪詢(xún)(polling):CPU 不斷的去讀硬件狀態(tài)。另一種是當(dāng)硬件完成某種事件后,給 CPU 一個(gè)中斷,讓 CPU 停下手上的事情,去處理這個(gè)中斷。很顯然,中斷的交互方式提高了系統(tǒng)的吞吐。
當(dāng) CPU 收到一個(gè)中斷 (IRQ)的時(shí)候,會(huì)去執(zhí)行該中斷對(duì)應(yīng)的處理函數(shù)(ISR)。普通情況下,會(huì)有一個(gè)中斷向量表,向量表中定義了 CPU 對(duì)應(yīng)的每一個(gè)外設(shè)資源的中斷處理程序的入口,當(dāng)發(fā)生對(duì)應(yīng)的中斷的時(shí)候, CPU 直接跳轉(zhuǎn)到這個(gè)入口執(zhí)行程序。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。
2. Linux 中斷 top/bottom
玩過(guò) MCU 的人都知道,中斷服務(wù)程序的設(shè)計(jì)最好是快速完成任務(wù)并退出,因?yàn)榇丝滔到y(tǒng)處于被中斷中。但是在 ISR 中又有一些必須完成的事情,比如:清中斷標(biāo)志,讀/寫(xiě)數(shù)據(jù),寄存器操作等。
在 Linux 中,同樣也是這個(gè)要求,希望盡快的完成 ISR。但事與愿違,有些 ISR 中任務(wù)繁重,會(huì)消耗很多時(shí)間,導(dǎo)致響應(yīng)速度變差。Linux 中針對(duì)這種情況,將中斷分為了兩部分:
- 上半部(top half):收到一個(gè)中斷,立即執(zhí)行,有嚴(yán)格的時(shí)間限制,只做一些必要的工作,比如:應(yīng)答,復(fù)位等。這些工作都是在所有中斷被禁止的情況下完成的。
- 底半部(bottom half):能夠被推遲到后面完成的任務(wù)會(huì)在底半部進(jìn)行。在適合的時(shí)機(jī),下半部會(huì)被開(kāi)中斷執(zhí)行。
3. 中斷處理程序
驅(qū)動(dòng)程序可以使用接口:
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
像系統(tǒng)申請(qǐng)注冊(cè)一個(gè)中斷處理程序。
其中的參數(shù):
中斷標(biāo)志 flag 的含義:
調(diào)用 request _irq 成功執(zhí)行返回 0。常見(jiàn)錯(cuò)誤是 -EBUSY,表示給定的中斷線已經(jīng)在使用(或者沒(méi)有指定 IRQF_SHARED)
注意:request_irq 函數(shù)可能引起睡眠,所以不允許在中斷上下文或者不允許睡眠的代碼中調(diào)用。
釋放中斷:
const void *free_irq(unsigned int irq, void *dev_id)
用于釋放中斷處理函數(shù)。
注意:Linux 中的中斷處理程序是無(wú)須重入的。當(dāng)給定的中斷處理程序正在執(zhí)行的時(shí)候,其中斷線在所有的處理器上都會(huì)被屏蔽掉,以防在同一個(gè)中斷線上又接收到另一個(gè)新的中斷。通常情況下,除了該中斷的其他中斷都是打開(kāi)的,也就是說(shuō)其他的中斷線上的重點(diǎn)都能夠被處理,但是當(dāng)前的中斷線總是被禁止的,故,同一個(gè)中斷處理程序是絕對(duì)不會(huì)被自己嵌套的。
4. 中斷上下文
與進(jìn)程上下文不一樣,內(nèi)核執(zhí)行中斷服務(wù)程序的時(shí)候,處于中斷上下文。中斷處理程序并沒(méi)有自己的獨(dú)立的棧,而是使用了內(nèi)核棧,其大小一般是有限制的(32bit 機(jī)器 8KB)。所以其必須短小精悍。同時(shí)中斷服務(wù)程序是打斷了正常的程序流程,這一點(diǎn)上也必須保證快速的執(zhí)行。同時(shí)中斷上下文中是不允許睡眠,阻塞的。
中斷上下文不能睡眠的原因是:
1、 中斷處理的時(shí)候,不應(yīng)該發(fā)生進(jìn)程切換,因?yàn)樵谥袛郼ontext中,唯一能打斷當(dāng)前中斷handler的只有更高優(yōu)先級(jí)的中斷,它不會(huì)被進(jìn)程打斷,如果在 中斷context中休眠,則沒(méi)有辦法喚醒它,因?yàn)樗械膚ake_up_xxx都是針對(duì)某個(gè)進(jìn)程而言的,而在中斷context中,沒(méi)有進(jìn)程的概念,沒(méi) 有一個(gè)task_struct(這點(diǎn)對(duì)于softirq和tasklet一樣),因此真的休眠了,比如調(diào)用了會(huì)導(dǎo)致block的例程,內(nèi)核幾乎肯定會(huì)死。
2、schedule()在切換進(jìn)程時(shí),保存當(dāng)前的進(jìn)程上下文(CPU寄存器的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容),以便以后恢復(fù)此進(jìn)程運(yùn)行。中斷發(fā)生后,內(nèi)核會(huì)先保存當(dāng)前被中斷的進(jìn)程上下文(在調(diào)用中斷處理程序后恢復(fù));
但在中斷處理程序里,CPU寄存器的值肯定已經(jīng)變化了吧(最重要的程序計(jì)數(shù)器PC、堆棧SP等),如果此時(shí)因?yàn)樗呋蜃枞僮髡{(diào)用了schedule(),則保存的進(jìn)程上下文就不是當(dāng)前的進(jìn)程context了.所以不可以在中斷處理程序中調(diào)用schedule()。
3、內(nèi)核中schedule()函數(shù)本身在進(jìn)來(lái)的時(shí)候判斷是否處于中斷上下文:
if(unlikely(in_interrupt()))
BUG();
因此,強(qiáng)行調(diào)用schedule()的結(jié)果就是內(nèi)核BUG。
4、中斷handler會(huì)使用被中斷的進(jìn)程內(nèi)核堆棧,但不會(huì)對(duì)它有任何影響,因?yàn)閔andler使用完后會(huì)完全清除它使用的那部分堆棧,恢復(fù)被中斷前的原貌。
5、處于中斷context時(shí)候,內(nèi)核是不可搶占的。因此,如果休眠,則內(nèi)核一定掛起。
5. 舉例
比如 RTC 驅(qū)動(dòng)程序 (drivers/char/rtc.c)。在 RTC 驅(qū)動(dòng)的初始化階段,會(huì)調(diào)用到 rtc_init 函數(shù):
module_init(rtc_init);
在這個(gè)初始化函數(shù)中調(diào)用到了 request_irq 用于申請(qǐng)中斷資源,并注冊(cè)服務(wù)程序:
static int __init rtc_init(void)
{
...
rtc_int_handler_ptr = rtc_interrupt;
...
request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)
...
}
RTC_IRQ 是中斷號(hào),和處理器綁定。
rtc_interrupt 是中斷處理程序:
static irqreturn_t rtc_interrupt(int irq, void dev_id)
{
/
- Can be an alarm interrupt, update complete interrupt,
- or a periodic interrupt. We store the status in the
- low byte and the number of interrupts received since
- the last read in the remainder of rtc_irq_data.
*/
spin_lock(&rtc_lock);
rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
if (is_hpet_enabled()) {
/*
* In this case it is HPET RTC interrupt handler
* calling us, with the interrupt information
* passed as arg1, instead of irq.
*/
rtc_irq_data |= (unsigned long)irq & 0xF0;
} else {
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
}
if (rtc_status & RTC_TIMER_ON)
mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
spin_unlock(&rtc_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
每次收到 RTC 中斷,就會(huì)調(diào)用進(jìn)這個(gè)函數(shù)。
6. 中斷處理流程
發(fā)生中斷時(shí),CPU執(zhí)行異常向量vector_irq的代碼, 即異常向量表中的中斷異常的代碼,它是一個(gè)跳轉(zhuǎn)指令,跳去執(zhí)行真正的中斷處理程序,在vector_irq里面,最終會(huì)調(diào)用中斷處理的總?cè)肟诤瘮?shù)。
C 語(yǔ)言的入口為 :asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
handle_IRQ(irq, regs);
}
該函數(shù)的入?yún)?irq 為中斷號(hào)。
asm_do_IRQ -> handle_IRQ
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
__handle_domain_irq(NULL, irq, false, regs);
}
handle_IRQ -> __handle_domain_irq
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
bool lookup, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
unsigned int irq = hwirq;
int ret = 0;
irq_enter();
#ifdef CONFIG_IRQ_DOMAIN
if (lookup)
irq = irq_find_mapping(domain, hwirq);
#endif
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(!irq || irq >= nr_irqs)) {
ack_bad_irq(irq);
ret = -EINVAL;
} else {
generic_handle_irq(irq);
}
irq_exit();
set_irq_regs(old_regs);
return ret;
}
這里請(qǐng)注意:
先調(diào)用了 irq_enter 標(biāo)記進(jìn)入了硬件中斷:
irq_enter是更新一些系統(tǒng)的統(tǒng)計(jì)信息,同時(shí)在__irq_enter宏中禁止了進(jìn)程的搶占。雖然在產(chǎn)生IRQ時(shí),ARM會(huì)自動(dòng)把CPSR中的I位置位,禁止新的IRQ請(qǐng)求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過(guò)local_irq_enable()打開(kāi)。那為何還要禁止搶占?這是因?yàn)橐紤]中斷嵌套的問(wèn)題,一旦流控層或驅(qū)動(dòng)程序主動(dòng)通過(guò)local_irq_enable打開(kāi)了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)作,所以才有了禁止搶占這一處理
再調(diào)用 generic_handle_irq
最后調(diào)用 irq_exit 刪除進(jìn)入硬件中斷的標(biāo)記
__handle_domain_irq -> generic_handle_irq
int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
generic_handle_irq_desc(desc);
return 0;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);
首先在函數(shù)irq_to_desc中根據(jù)發(fā)生中斷的中斷號(hào),去取出它的 irq_desc 中斷描述結(jié)構(gòu),然后調(diào)用 generic_handle_irq_desc:
static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
desc->handle_irq(desc);
}
這里調(diào)用了 handle_irq 函數(shù)。
所以,在上述流程中,還需要分析 irq_to_desc 流程:
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
EXPORT_SYMBOL(irq_to_desc);
NR_IRQS 是支持的總的中斷個(gè)數(shù),當(dāng)然,irq 不能夠大于這個(gè)數(shù)目。所以返回 irq_desc + irq。
irq_desc 是一個(gè)全局的數(shù)組:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
這里是這個(gè)數(shù)組的初始化的地方。所有的 handle_irq 函數(shù)都被初始化成為了 handle_bad_irq。
細(xì)心的觀眾可能發(fā)現(xiàn)了,調(diào)用這個(gè) desc->handle_irq(desc) 函數(shù),并不是咱們注冊(cè)進(jìn)去的中斷處理函數(shù)啊,因?yàn)閮蓚€(gè)函數(shù)的原型定義都不一樣。這個(gè) handle_irq 是 irq_flow_handler_t 類(lèi)型,而我們注冊(cè)進(jìn)去的服務(wù)程序是 irq_handler_t,這兩個(gè)明顯不是同一個(gè)東西,所以這里我們還需要繼續(xù)分析。
6.1 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)
Linux 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)有 3 個(gè)
irq_desc 結(jié)構(gòu)如下
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction action; / IRQ action list /
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; / nested irq disables /
unsigned int wake_depth; / nested wake enables /
unsigned int irq_count; / For detecting broken IRQs /
unsigned long last_unhandled; / Aging timer for unhandled count */
unsigned int irqs_unhandled;
atomic_t threads_handled;
int threads_handled_last;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
const struct cpumask *percpu_affinity;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PM_SLEEP
unsigned int nr_actions;
unsigned int no_suspend_depth;
unsigned int cond_suspend_depth;
unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file;
const char *dev_name;
#endif
#ifdef CONFIG_SPARSE_IRQ
struct rcu_head rcu;
struct kobject kobj;
#endif
struct mutex request_mutex;
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
irqaction 結(jié)構(gòu)如下:
/**
- struct irqaction - per interrupt action descriptor
- @handler: interrupt handler function
- @name: name of the device
- @dev_id: cookie to identify the device
- @percpu_dev_id: cookie to identify the device
- @next: pointer to the next irqaction for shared interrupts
- @irq: interrupt number
- @flags: flags (see IRQF_* above)
- @thread_fn: interrupt handler function for threaded interrupts
- @thread: thread pointer for threaded interrupts
- @secondary: pointer to secondary irqaction (force threading)
- @thread_flags: flags related to @thread
- @thread_mask: bitmask for keeping track of @thread activity
- @dir: pointer to the proc/irq/NN/name entry
*/
struct irqaction {
irq_handler_t handler;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
struct irqaction *secondary;
unsigned int irq;
unsigned int flags;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
irq_chip 描述如下:
/**
struct irq_chip - hardware interrupt chip descriptor
@parent_device: pointer to parent device for irqchip
@name: name for /proc/interrupts
@irq_startup: start up the interrupt (defaults to ->enable if NULL)
@irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
@irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
@irq_disable: disable the interrupt
@irq_ack: start of a new interrupt
@irq_mask: mask an interrupt source
@irq_mask_ack: ack and mask an interrupt source
@irq_unmask: unmask an interrupt source
@irq_eoi: end of interrupt
@irq_set_affinity: Set the CPU affinity on SMP machines. If the force
argument is true, it tells the driver to
unconditionally apply the affinity setting. Sanity
checks against the supplied affinity mask are not
required. This is used for CPU hotplug where the
target CPU is not yet set in the cpu_online_mask.
@irq_retrigger: resend an IRQ to the CPU
@irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
@irq_set_wake: enable/disable power-management wake-on of an IRQ
@irq_bus_lock: function to lock access to slow bus (i2c) chips
@irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
@irq_cpu_online: configure an interrupt source for a secondary CPU
@irq_cpu_offline: un-configure an interrupt source for a secondary CPU
@irq_suspend: function called from core code on suspend once per
chip, when one or more interrupts are installed
@irq_resume: function called from core code on resume once per chip,
when one ore more interrupts are installed
@irq_pm_shutdown: function called from core code on shutdown once per chip
@irq_calc_mask: Optional function to set irq_data.mask for special cases
@irq_print_chip: optional to print special chip info in show_interrupts
@irq_request_resources: optional to request resources before calling
any other callback related to this irq
@irq_release_resources: optional to release resources acquired with
irq_request_resources
@irq_compose_msi_msg: optional to compose message content for MSI
@irq_write_msi_msg: optional to write message content for MSI
@irq_get_irqchip_state: return the internal state of an interrupt
@irq_set_irqchip_state: set the internal state of a interrupt
@irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
@ipi_send_single: send a single IPI to destination cpus
@ipi_send_mask: send an IPI to destination cpus in cpumask
@flags: chip specific flags
*/
struct irq_chip {
struct device *parent_device;
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_calc_mask)(struct irq_data *data);
void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
int (*irq_request_resources)(struct irq_data *data);
void (*irq_release_resources)(struct irq_data *data);void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);unsigned long flags;
};
irq_chip 是一串和芯片相關(guān)的函數(shù)指針,這里定義的非常的全面,基本上和 IRQ 相關(guān)的可能出現(xiàn)的操作都全部定義進(jìn)去了,具體根據(jù)不同的芯片,需要在不同的芯片的地方去初始化這個(gè)結(jié)構(gòu),然后這個(gè)結(jié)構(gòu)會(huì)嵌入到通用的 IRQ 處理軟件中去使用,使得軟件處理邏輯和芯片邏輯完全的分開(kāi)。
好,我們接下來(lái)繼續(xù)前進(jìn)。
6.2 初始化 Chip 相關(guān)的 IRQ
眾所周知,啟動(dòng)的時(shí)候,C 語(yǔ)言從 start_kernel 開(kāi)始,在這里面,調(diào)用了和 machine 相關(guān)的 IRQ 的初始化 init_IRQ():
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes;
.....
early_irq_init();
init_IRQ();
.....
}
在 init_IRQ 中,調(diào)用了machine_desc->init_irq():
void __init init_IRQ(void)
{
int ret;
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else
machine_desc->init_irq();
if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
(machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
if (!outer_cache.write_sec)
outer_cache.write_sec = machine_desc->l2c_write_sec;
ret = l2x0_of_init(machine_desc->l2c_aux_val,
machine_desc->l2c_aux_mask);
if (ret && ret != -ENODEV)
pr_err("L2C: failed to init: %dn", ret);
}
uniphier_cache_init();
}
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)。
這里初始化的時(shí)候回調(diào)用到具體的芯片相關(guān)的中斷初始化的地方。
例如:
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;
}
而在這些里面,都回去調(diào)用類(lèi)似于:
void
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle, const char *name);
irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
{
__irq_set_handler(irq, handle, 0, NULL);
}
static inline void
irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
{
__irq_set_handler(irq, handle, 1, NULL);
}
void
irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
void *data);
這些函數(shù)定義在 include/linux/irq.h 文件。是對(duì)芯片初始化的時(shí)候可見(jiàn)的 APIs,用于指定中斷“流控”中的 :
irq_flow_handler_t handle
也就是中斷來(lái)的時(shí)候,最后那個(gè)函數(shù)調(diào)用。
中斷流控函數(shù),分幾種,電平觸發(fā)的中斷,邊沿觸發(fā)的,等:
/*
- Built-in IRQ handlers for various IRQ types,
- callable via desc->handle_irq()
*/
extern void handle_level_irq(struct irq_desc *desc);
extern void handle_fasteoi_irq(struct irq_desc *desc);
extern void handle_edge_irq(struct irq_desc *desc);
extern void handle_edge_eoi_irq(struct irq_desc *desc);
extern void handle_simple_irq(struct irq_desc *desc);
extern void handle_untracked_irq(struct irq_desc *desc);
extern void handle_percpu_irq(struct irq_desc *desc);
extern void handle_percpu_devid_irq(struct irq_desc *desc);
extern void handle_bad_irq(struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq);
而在這些處理函數(shù)里,會(huì)去調(diào)用到 :handle_irq_event
比如:
/**
handle_level_irq - Level type irq handler
@desc: the interrupt description structure for this irq
Level type interrupts are active as long as the hardware line has
the active level. This may require to mask the interrupt and unmask
it after the associated handler has acknowledged the device, so the
interrupt line is back to inactive.
*/
void handle_level_irq(struct irq_desc *desc)
{
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);if (!irq_may_run(desc))
goto out_unlock;desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
/*
- If its disabled or no action available
- keep it masked and get out of here
*/
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
kstat_incr_irqs_this_cpu(desc);
handle_irq_event(desc);cond_unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock);
}
而這個(gè) handle_irq_event 則是調(diào)用了處理,handle_irq_event_percpu:
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
irqreturn_t ret;
desc->istate &= ~IRQS_PENDING;
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
raw_spin_unlock(&desc->lock);
ret = handle_irq_event_percpu(desc);
raw_spin_lock(&desc->lock);
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
return ret;
}
handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()】
這里終于看到了調(diào)用 的地方了,就是咱們通過(guò) request_irq 注冊(cè)進(jìn)去的函數(shù)
7. /proc/interrupts
這個(gè) proc 下放置了對(duì)應(yīng)中斷號(hào)的中斷次數(shù)和對(duì)應(yīng)的 dev-name
-
cpu
+關(guān)注
關(guān)注
68文章
10702瀏覽量
209450 -
Linux
+關(guān)注
關(guān)注
87文章
11125瀏覽量
207936 -
程序
+關(guān)注
關(guān)注
115文章
3720瀏覽量
80366 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4238瀏覽量
61976
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論