| |
中断管理系统的初始化
我们先来看一下Linux系统中,中断管理系统的初始化。中断系统的初始化主要由两个函数来完成。在系统初始化的start_kernel()函数 (在文件init/main.c中定义)中可以看到:
asmlinkage void __init start_kernel(void)
{
……
trap_init();
……
early_irq_init();
init_IRQ();
……
}
start_kernel()函数调用early_irq_init()和init_IRQ()两个函数来初始化中断管理系统。
early_irq_init()函数
在start_kernel()函数中调用了early_irq_init()函数,这个函数在kernel/handle.c文件中定义。这个函数将用于管理中断的irq_desc[NR_IRQS]数组的每个元素的部分字段设置为确定的状态,它设置每一个成员的中断号,
int __init early_irq_init(void)
{
struct irq_desc *desc;
int count;
int i;
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 = i;
alloc_desc_masks(&desc[i], 0, true);
init_desc_masks(&desc[i]);
desc[i].kstat_irqs = kstat_irqs_all[i];
}
return arch_early_irq_init();
}
init_IRQ()函数
init_IRQ(void)函数是一个特定与体系结构的函数,对于ARM体系结构,在文件arch/arm/kernel/irq.c中定义,这个函数将用于管理中断的irq_desc[NR_IRQS]结构数组各成员的状态字段设置为IRQ_NOREQUEST | IRQ_NOPROBE,也就是未请求和未探测状态。然后调用特定平台的中断初始化的init_arch_irq()函数:
void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
init_arch_irq();
}
而init_arch_irq()实际上是一个函数指针,同样在文件arch/arm/kernel/irq.c中定义:
void (*init_arch_irq)(void) __initdata = NULL;
这个指针由函数setup_arch()初始化,而setup_arch()也是在start_kernel()中调用的。setup_arch()也是一个特定于体系架构的函数,在arch/arm/kernel/setup.c中定义:
void __init setup_arch(char **cmdline_p)
{
……
init_arch_irq = mdesc->init_irq;
……
}
init_arch_irq指针被初始化为mdesc结构的init_irq成员,这是一个struct machine_desc类型的结构,mach_desc里定义了一些关键的体系架构相关的信息。以mini2440为例,其machine_desc结构的init_irq成员在文件arch/arm/mach-s
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet <buserror@gmail.com> */
.phys_io = S
.io_pg_offst = (((u32)S
.boot_params = S
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s
.timer = &s
MACHINE_END
注:MACHINE_START宏的作用是对mach_desc结构体进行初始化。OK,终于找到了init_IRQ() 函数中调用的init_arch_irq()函数的真正内容,在arch/arm/plat-s
/* s
*
* Initialise S
*/
void __init s
{
unsigned long pend;
unsigned long last;
int irqno;
int i;
#ifdef CONFIG_FIQ
init_FIQ();
#endif
irqdbf("s
/* first, clear all interrupts pending... */
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S
if (pend == 0 || pend == last)
break;
__raw_writel(pend, S
printk("irq: clearing pending ext status %08x\n", (int)pend);
last = pend;
}
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S
if (pend == 0 || pend == last)
break;
__raw_writel(pend, S
__raw_writel(pend, S
printk("irq: clearing pending status %08x\n", (int)pend);
last = pend;
}
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S
if (pend == 0 || pend == last)
break;
printk("irq: clearing subpending status %08x\n", (int)pend);
__raw_writel(pend, S
last = pend;
}
/* register the main interrupts */
irqdbf("s
for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
/* set all the s
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:
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_level_irq);
break;
case IRQ_RESERVED6:
case IRQ_RESERVED24:
/* no IRQ here */
break;
default:
//irqdbf("registering irq %d (s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
}
/* setup the cascade irq handlers */
set_irq_chained_handler(IRQ_EINT4t7, s
set_irq_chained_handler(IRQ_EINT8t23, s
set_irq_chained_handler(IRQ_UART0, s
set_irq_chained_handler(IRQ_UART1, s
set_irq_chained_handler(IRQ_UART2, s
set_irq_chained_handler(IRQ_ADCPARENT, s
/* external interrupts */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
/* register the uart interrupts */
irqdbf("s
for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
irqdbf("registering irq %d (s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
irqdbf("registering irq %d (s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
irqdbf("registering irq %d (s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
irqdbf("registering irq %d (s
set_irq_chip(irqno, &s
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
irqdbf("s
}
这个函数完成对于中断控制器的初始化,并且设置中断描述符的相应的函数指针的值,以在中断发生时发生时,调用这些函数来完成芯片级的处理。
这个函数又调用了许多相关的函数来初始化中断描述。先看set_irq_chip()函数,在文件kernel/irq/chip.c中定义:
/**
* set_irq_chip - set the irq chip for an irq
* @irq: irq number
* @chip: pointer to irq chip description structure
*/
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
// 获取对应于中断号irq的中断描述符
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc) {
WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
return -EINVAL;
}
if (!chip)
chip = &no_irq_chip;
spin_lock_irqsave(&desc->lock, flags);
// 为chip设置一些默认的enable,disable函数
irq_chip_set_defaults(chip);
desc->chip = chip;
spin_unlock_irqrestore(&desc->lock, flags);
return 0;
}
设置中断描述符的chip,以在中断发生或者管理中断时可以完成对于芯片的操作。
以IRQ_EINT4t7为例,它的chip是s
struct irq_chip s
.name = "s
.ack = s
.mask = s
.unmask = s
.set_wake = s
};
再看irq_chip_set_defaults()函数,在kernel/irq/chip.c中定义:
/*
* Fixup enable/disable function pointers
*/
void irq_chip_set_defaults(struct irq_chip *chip)
{
if (!chip->enable)
chip->enable = default_enable;
if (!chip->disable)
chip->disable = default_disable;
if (!chip->startup)
chip->startup = default_startup;
/*
* We use chip->disable, when the user provided its own. When
* we have default_disable set for chip->disable, then we need
* to use default_shutdown, otherwise the irq line is not
* disabled on free_irq():
*/
if (!chip->shutdown)
chip->shutdown = chip->disable != default_disable ?
chip->disable : default_shutdown;
if (!chip->name)
chip->name = chip->typename;
if (!chip->end)
chip->end = dummy_irq_chip.end;
}
如果chip没有相应的操作函数,则就给chip赋默认的操作函数。
接着看set_irq_handler()函数,在include/linux/irq.h中定义:
/*
* Set a highlevel flow handler for a given IRQ:
*/
static inline void
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
__set_irq_handler(irq, handle, 0, NULL);
}
然后来看__set_irq_handler()函数,在kernel/irq/chip.c中:
void
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc) {
printk(KERN_ERR
"Trying to install type control for IRQ%d\n", irq);
return;
}
if (!handle)
handle = handle_bad_irq;
else if (desc->chip == &no_irq_chip) {
printk(KERN_WARNING "Trying to install %sinterrupt handler "
"for IRQ%d\n", is_chained ? "chained " : "", irq);
/*
* Some ARM implementations install a handler for really dumb
* interrupt hardware without setting an irq_chip. This worked
* with the ARM no_irq_chip but the check in setup_irq would
* prevent us to setup the interrupt at all. Switch it to
* dummy_irq_chip for easy transition.
*/
desc->chip = &dummy_irq_chip;
}
chip_bus_lock(irq, desc);
spin_lock_irqsave(&desc->lock, flags);
/* Uninstall? */
if (handle == handle_bad_irq) {
if (desc->chip != &no_irq_chip)
mask_ack_irq(desc, irq);
desc->status |= IRQ_DISABLED;
desc->depth = 1;
}
desc->handle_irq = handle;
desc->name = name;
if (handle != handle_bad_irq && is_chained) {
desc->status &= ~IRQ_DISABLED;
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
desc->depth = 0;
desc->chip->startup(irq);
}
spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
}
上面这个函数就是为特定的中断号设置好一个中断处理例程,这里的例程可不是我们request_irq注册的例程,Linux支持中断共享,共享同一个中断号的每一设备都可以有自己特定的中断处理程序,用来描述这些中断处理程序的结构会形成一个链表,这里设置的例程将会逐个调用特定中断号上的各设备的中断处理例程。
接着看set_irq_flags()函数,在