三读内核中断处理(1):数据结构

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

本文适用于

ADSP-BF561

优视BF561EVB开发板

uclinux-2008r1.5-rc3(smp patch)

Visual DSP++ 5.0(update 5)

欢迎转载,但请保留作者信息

1.1 irq_desc

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_irqchip这两个成员。这里需要注意的是NR_IRQS的定义。在bf561中,提供了64个外部中断和6个内部中断,其中60-634个外部中断保留。但是NR_IRQS的定义高达121。其构成为:

l 7个内部中断,EVT0EVT6

l 64个外部中断源,包括4bf561保留的外部中断。

l 软件中断14和系统调用中断15,这两个中断由系统保留,没有外部中断映射到这两个中断上,因此为它们单独留下描述信息。

l PF口,这48PF口仅仅用了6个外部中断源,在内核中为这48PF口每个都留了中断描述。

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_INTAIRQ_PROG1_INTAIRQ_PROG2_INTA这三个由PF口共享的中断则使用bf561_demux_gpio_irq进行处理;对于其它的中断源则使用handle_simple_irq进行处理。

1.2 irqaction

这一结构体定义为:

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数组中去。

1.3 irq_chip

内核使用一个称为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)

uclinuxbf561的中断分为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,

};

对于这类中断,对它进行maskunmask操作时必须直接操作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

};

对这类中断,需要进行特殊处理,在后面再进行分析。

1.4 ivg_table

为了记录BF56164个外部中断的状态,内核使用了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_MASKSICx_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];

此结构体中的ifirstistop都指向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++;

}

}

}

}

你可能感兴趣的:(数据结构,linux,Access,UP,UML)