當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > Linux中斷子系統(tǒng)的初始化
Linux中斷子系統(tǒng)的初始化
注:以2.6.39內(nèi)核源碼講解
Linux整個(gè)中斷處理體系其實(shí)可以分為兩個(gè)部分,一部分是系統(tǒng)完成的部分,另一部分是驅(qū)動(dòng)工程師需要完成的部分(也就是我們用requst_irq注冊(cè)的處理函數(shù)),本次我們主要討論的是系統(tǒng)啟動(dòng)的過程中對(duì)中斷子系統(tǒng)做了哪些事?
一、搬移異常向量表
內(nèi)核在啟動(dòng)的時(shí)候就是先運(yùn)行start_kernel() , 然后她就會(huì)調(diào)用體系結(jié)構(gòu)相關(guān)的setup_arch(&command_line), 如arm體系結(jié)構(gòu)的在arch/arm/kernel/setup.c中, 進(jìn)一步, 她就要初始化板級(jí)相關(guān)的設(shè)備,內(nèi)核會(huì)在這個(gè)工程中調(diào)用early_trap_init()把異常向量表搬移到0xFFFF0000的位置。
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc;
setup_processor();
mdesc = setup_machine(machine_arch_type);
......
early_trap_init(); //把異常向量表搬移到0xFFFF0000的位置。
}
二、初始化中斷管理子系統(tǒng)
我們先來看一下Linux系統(tǒng)中,中斷管理系統(tǒng)的初始化。中斷系統(tǒng)的初始化主要由兩個(gè)函數(shù)來完成。在系統(tǒng)初始化的start_kernel()函數(shù) (在文件init/main.c中定義)中可以看到:
asmlinkage void __init start_kernel(void)
{
……
setup_arch(&command_line);
trap_init();
……
early_irq_init();
init_IRQ();
……
}
Linux中斷機(jī)制的核心數(shù)據(jù)結(jié)構(gòu) irq_desc, 它完整地描述了一條中斷線 (或可簡(jiǎn)單理解為 “一個(gè)中斷源” )。start_kernel()函數(shù)調(diào)用early_irq_init()和init_IRQ()兩個(gè)函數(shù)來初始化中斷管理系統(tǒng)。其實(shí)就是初始化irq_desc的結(jié)構(gòu)。
1、early_irq_init()函數(shù)
注:這個(gè)函數(shù)在kernel/irq/irqdesc.c文件中定義。
在start_kernel()函數(shù)中調(diào)用了early_irq_init()函數(shù),linux調(diào)用early_irq_init()初始化linux中斷系統(tǒng)的核心數(shù)據(jù)。early_irq_init屬于與硬件和平臺(tái)無關(guān)的通用邏輯層,為irq_desc[]中個(gè)元素的某些成員填充默認(rèn)值,完成后調(diào)用體系相關(guān)的arch_early_irq_init函數(shù)完成進(jìn)一步的初始化工作,不過ARM體系沒有實(shí)現(xiàn)arch_early_irq_init。
注:irq_desc[] 全局?jǐn)?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),
}
};
int __init early_irq_init(void)
{
int count, i, node = first_online_node;
struct irq_desc *desc;
init_irq_default_affinity();
printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
desc = irq_desc;
count = ARRAY_SIZE(irq_desc);
for (i = 0; i < count; i++) {
desc[i].irq_data.irq = i;
desc[i].irq_data.chip = &no_irq_chip;
desc[i].kstat_irqs = alloc_percpu(unsigned int);
irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
alloc_masks(desc + i, GFP_KERNEL, node);
desc_smp_init(desc + i, node);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
}
return arch_early_irq_init();
}
注: 在使用設(shè)備樹的高版本內(nèi)核中,管理中斷描述符也可能是用基數(shù)樹去管理了,避免空間的浪費(fèi)。
2、init_IRQ()函數(shù)
init_IRQ(void)函數(shù)是一個(gè)特定與體系結(jié)構(gòu)的函數(shù),對(duì)于ARM體系結(jié)構(gòu),在文件arch/arm/kernel/irq.c
void __init init_IRQ(void)
{
machine_desc->init_irq();
}
mdesc結(jié)構(gòu)的init_irq成員,這是一個(gè)struct machine_desc類型的結(jié)構(gòu),mach_desc里定義了一些關(guān)鍵的體系架構(gòu)相關(guān)的信息。
以mini2440為例:
其machine_desc結(jié)構(gòu)的init_irq成員在文件arch/arm/mach-s3c2440/mach-mini2440.c中被賦值為s3c24xx_init_irq函數(shù)
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet <buserror@gmail.com> */
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c24xx_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
注:MACHINE_START宏的作用是對(duì)mach_desc結(jié)構(gòu)體進(jìn)行初始化。OK,終于找到了init_IRQ() 真正內(nèi)容,在arch/arm/plat-s3c24xx/irq.c中定義:
/* s3c24xx_init_irq
*
* Initialise S3C2410 IRQ system
*/
3、init_IRQ()實(shí)例
對(duì)應(yīng)平臺(tái)下面的代碼,和硬件平臺(tái)相關(guān),為各個(gè)中斷源設(shè)置默認(rèn)的處理函數(shù)
void __init s3c24xx_init_irq(void)
{
unsigned long pend;
unsigned long last;
int irqno;
int i;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S3C24XX_EINTPEND);
if (pend == 0 || pend == last)
break;
__raw_writel(pend, S3C24XX_EINTPEND);
printk("irq: clearing pending ext status %08x\n", (int)pend);
last = pend;
}
.......
for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
/* set all the s3c2410 internal irqs */
switch (irqno) {
/* deal with the special IRQs (cascaded) */
case IRQ_EINT4t7:
case IRQ_EINT8t23:
case IRQ_UART0:
case IRQ_UART1:
case IRQ_UART2:
case IRQ_ADCPARENT:
irq_set_chip_and_handler(irqno, &s3c_irq_level_chip, handle_level_irq);
break;
case IRQ_RESERVED6:
case IRQ_RESERVED24:
/* no IRQ here */
break;
default:
//irqdbf("registering irq %d (s3c irq)\n", irqno);
irq_set_chip_and_handler(irqno, &s3c_irq_chip, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
}
/* 設(shè)置外部中斷的控制器(操作方法集),默認(rèn)前級(jí)處理函數(shù)handle_irq */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irq_set_chip_and_handler(irqno, &s3c_irqext_chip, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
......
}
這個(gè)函數(shù)完成對(duì)于中斷控制器的初始化,并且設(shè)置中斷描述符的相應(yīng)的函數(shù)指針的值,以在中斷發(fā)生時(shí)發(fā)生時(shí),調(diào)用這些函數(shù)來完成芯片級(jí)的處理。
irq_set_chip_and_handler()介紹:
1、可以設(shè)置中斷號(hào)對(duì)應(yīng)的中斷描述符的chip,里面其實(shí)是一系列操作對(duì)于控制器的方法,以在中斷發(fā)生或者管理中斷時(shí)可以完成對(duì)于芯片的操作。
以IRQ_EINT4-IRQ_EINT23為例,它的chip是s3c_irqext_chip
static struct irq_chip s3c_irqext_chip = {
.name = "s3c-ext",
.irq_mask = s3c_irqext_mask,
.irq_unmask = s3c_irqext_unmask,
.irq_ack = s3c_irqext_ack,
.irq_set_type = s3c_irqext_type,
.irq_set_wake = s3c_irqext_wake
};
2、可以設(shè)置中斷發(fā)生的第一級(jí)處理函數(shù)比如 handle_level_irq 或者 handle_edge_irq,本質(zhì)上就是設(shè)置irq_desc[irqno].handle_irq 的值。
其實(shí)中斷處理的時(shí)候一般分為兩級(jí),第一級(jí)是調(diào)用irq_desc[irqno].handle_irq。它主要面向GIC的莫一中斷線IRQ line,跟當(dāng)前中斷觸發(fā)電信號(hào)相關(guān)的一個(gè)函數(shù),不同的中斷觸發(fā)方式,其中斷線處理的函數(shù)是不同的,不過主要是電平處理和邊沿處理兩種。 第二級(jí)才是我們驅(qū)動(dòng)開發(fā)者自己注冊(cè)的中斷函數(shù),也就是irq_desc[irqno].action->handler的調(diào)用。
上面這個(gè)函數(shù)就是為特定的中斷號(hào)設(shè)置好一個(gè)中斷處理例程,這里的例程可不是我們r(jià)equest_irq注冊(cè)的例程,Linux支持中斷共享,共享同一個(gè)中斷號(hào)的每一設(shè)備都可以有自己特定的中斷處理程序,用來描述這些中斷處理程序的結(jié)構(gòu)會(huì)形成一個(gè)鏈表,這里設(shè)置的例程將會(huì)逐個(gè)調(diào)用特定中斷號(hào)上的各設(shè)備的中斷處理例程。