快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADSP-BF561
uclinux-2008r1.5-rc3(smp patch)
Visual DSP++ 5.0(update 5)
欢迎转载,但请保留作者信息
uclinux内核为每一个中断设置了一个称为irq_desc的结构体进行描述,这个结构体与硬件无关:
/**
* struct irq_desc - interrupt descriptor
*
* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
* @chip: low level interrupt hardware access
* @msi_desc: MSI descriptor
* @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
* @action: the irq action chain
* @status: status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity: IRQ affinity on SMP
* @cpu: cpu index useful for balancing
* @pending_mask: pending rebalanced interrupts
* @dir: /proc/irq/ procfs entry
* @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
extern struct irq_desc irq_desc[NR_IRQS];
对它的各个成员的描述其注释已经比较清楚了,最主要的是handle_irq和chip这两个成员。这里需要注意的是NR_IRQS的定义。在bf561中,提供了64个外部中断和6个内部中断,其中60-63这4个外部中断保留。但是NR_IRQS的定义高达121。其构成为:
l 7个内部中断,EVT0到EVT6。
l 64个外部中断源,包括4个bf561保留的外部中断。
l 软件中断14和系统调用中断15,这两个中断由系统保留,没有外部中断映射到这两个中断上,因此为它们单独留下描述信息。
l PF口,这48个PF口仅仅用了6个外部中断源,在内核中为这48个PF口每个都留了中断描述。
irq_desc这个数组的定义在kernel/irq/handle.c中:
/*
* Linux has a controller-independent interrupt architecture.
* Every controller has a 'controller-template', that is used
* by the main code to do the right thing. Each driver-visible
* interrupt source is transparently wired to the appropriate
* controller. Thus drivers need not be aware of the
* interrupt-controller.
*
* The code is designed to be easily extended with new/different
* interrupt controllers, without having to do assembly magic or
* having to touch the generic code.
*
* Controller mappings for all interrupt sources:
*/
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
}
};
对于不同中断源的默认处理函数(irq_desc结构体中的handle_irq),内核将之分成了3类。对于PF0-PF47的中断,将调用handle_level_irq进行处理;对于IRQ_PROG0_INTA、IRQ_PROG1_INTA、IRQ_PROG2_INTA这三个由PF口共享的中断则使用bf561_demux_gpio_irq进行处理;对于其它的中断源则使用handle_simple_irq进行处理。
这一结构体定义为:
struct irqaction {
irq_handler_t handler;
unsigned long flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
};
当有代码请求设置一个中断的回调函数时,中断管理程序将创建一个irqaction对象,然后将之放到相应的irq_desc数组中去。
内核使用一个称为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 bf561_core_irqchip = {
.name = "CORE",
.ack = ack_noop,
.mask = bf561_core_mask_irq,
.unmask = bf561_core_unmask_irq,
};
对于这类中断,对它进行mask和unmask操作时必须直接操作IMASK寄存器。
第二类中断是没有共用的外部中断,内核使用下述结构体进行控制:
static struct irq_chip bf561_internal_irqchip = {
.name = "INTN",
.ack = ack_noop,
.mask = bf561_internal_mask_irq,
.unmask = bf561_internal_unmask_irq,
};
对于这类中断,直接操作SIC_IMASK。
第三类中断是共用的外部中断,如PF0-PF7就共用一个中断源,此时内核使用下述结构体进行控制:
static struct irq_chip bf561_gpio_irqchip = {
.name = "GPIO",
.ack = bf561_gpio_ack_irq,
.mask = bf561_gpio_mask_irq,
.mask_ack = bf561_gpio_mask_ack_irq,
.unmask = bf561_gpio_unmask_irq,
.set_type = bf561_gpio_irq_type,
.startup = bf561_gpio_irq_startup,
.shutdown = bf561_gpio_irq_shutdown
};
对这类中断,需要进行特殊处理,在后面再进行分析。
为了记录BF561的64个外部中断的状态,内核使用了ivg_table的数组:
#define NR_PERI_INTS 64
struct ivgx {
/* irq number for request_irq, available in mach-bf561/irq.h */
int irqno;
/* corresponding bit in the SICA_ISR0 register */
int isrflag0;
/* corresponding bit in the SICA_ISR1 register */
int isrflag1;
} ivg_table[NR_PERI_INTS];
在这里,ivg_table的数组是使用内部中断号来进行排序的,即从IVG7排到IVG13。而其中的irqno这个值保存的是内核中的中断序号,使用这个序号即可访问irq_desc数组中对此中断的描述。isrflags则存放的是此中断对于于SICx_MASK、SICx_ISR等的掩码。
为了快速访问同一个内部中断号所对应的所有外部中断,内核使用了另一个数组:
struct ivg_slice {
/* position of first irq in ivg_table for given ivg */
struct ivgx *ifirst;
struct ivgx *istop;
} ivg7_13[IVG13 - IVG7 + 1];
此结构体中的ifirst和istop都指向ivg_table,由于ivg_table的元素是按内部中断号升序排列的,因而可以用ifirst指向相应内部中断号的起始元素,用istop指向结束元素。
在内核初始化的时候会调用一个称之为search_IAR的函数,从中可以看出这两个结构体之间的关系:
/*
* Search SIC_IAR and fill tables with the irqvalues
* and their positions in the SIC_ISR register.
*/
static void __init search_IAR(void)
{
unsigned ivg, irq_pos = 0;
for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) {
int irqn;
ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos];
for (irqn = 0; irqn < NR_PERI_INTS; irqn++) {
int iar_shift = (irqn & 7) * 4;
if (ivg ==
(0xf &
bfin_read32((unsigned long *)SICA_IAR0 +
(irqn >> 3)) >> iar_shift)) {
ivg_table[irq_pos].irqno = IVG7 + irqn;
ivg_table[irq_pos].isrflag0 =
(irqn < 32 ? (1 << irqn) : 0);
ivg_table[irq_pos].isrflag1 =
(irqn < 32 ? 0 : (1 << (irqn - 32)));
ivg7_13[ivg].istop++;
irq_pos++;
}
}
}
}