内核使用一个称为irq_chip的结构体来描述中断控制器,对中断的操作(启用/禁用某个中断源)将通过irq_chip的回调函数进行,此结构体的定义位于include/linux/irq.h:
/**
* struct irq_chip - hardware interrupt chip descriptor
*
* @name: name for /proc/interrupts
* @startup: start up the interrupt (defaults to ->enable if NULL)
* @shutdown: shut down the interrupt (defaults to ->disable if NULL)
* @enable: enable the interrupt (defaults to chip->unmask if NULL)
* @disable: disable the interrupt (defaults to chip->mask if NULL)
* @ack: start of a new interrupt
* @mask: mask an interrupt source
* @mask_ack: ack and mask an interrupt source
* @unmask: unmask an interrupt source
* @eoi: end of interrupt - chip level
* @end: end of interrupt - flow level
* @set_affinity: set the CPU affinity on SMP machines
* @retrigger: resend an IRQ to the CPU
* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @set_wake: enable/disable power-management wake-on of an IRQ
*
* @release: release function solely used by UML
* @typename: obsoleted by name, kept as migration helper
*/
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
void (*set_affinity)(unsigned int irq, cpumask_t dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
对系统中的每一个中断,都必须有一个与它相关的irq_chip进行操作(irq_desc->chip)。
uclinux将bf561的中断分为3类,使用了3个不同的irq_chip来对它们进行控制。
第一类是内部中断,即中断0到中断6,内核使用下述结构体进行控制:
static struct irq_chip bfin_core_irqchip = {
.ack = ack_noop,
.mask = bfin_core_mask_irq,
.unmask = bfin_core_unmask_irq,
};
对于这类中断,对它进行mask和unmask操作时必须直接操作IMASK。
第二类中断是没有共用的外部中断,内核使用下述结构体进行控制:
static struct irq_chip bfin_internal_irqchip = {
.ack = ack_noop,
.mask = bfin_internal_mask_irq,
.unmask = bfin_internal_unmask_irq,
.mask_ack = bfin_internal_mask_irq,
.disable = bfin_internal_mask_irq,
.enable = bfin_internal_unmask_irq,
#ifdef CONFIG_PM
.set_wake = bfin_internal_set_wake,
#endif
};
对于这类中断,直接操作SIC_IMASK。
第三类中断是共用的外部中断,如PF0-PF7就共用一个中断源,此时内核使用下述结构体进行控制:
static struct irq_chip bfin_gpio_irqchip = {
.ack = bfin_gpio_ack_irq,
.mask = bfin_gpio_mask_irq,
.mask_ack = bfin_gpio_mask_ack_irq,
.unmask = bfin_gpio_unmask_irq,
.set_type = bfin_gpio_irq_type,
.startup = bfin_gpio_irq_startup,
.shutdown = bfin_gpio_irq_shutdown,
#ifdef CONFIG_PM
.set_wake = bfin_gpio_set_wake,
#endif
};
对这类中断,需要进行特殊处理,在后面再进行分析。
对于不同中断源的默认处理函数(irq_chip结构体中的handle),内核同样分成了3类。对于PF0-PF47的中断,将调用handle_level_irq进行处理;对于IRQ_PROG0_INTA、IRQ_PROG1_INTA、IRQ_PROG2_INTA这三个由PF口共享的中断则使用bfin_demux_gpio_irq进行处理;对于其它的中断源则使用handle_simple_irq进行处理。