您的位置:首页 > 运维架构 > Linux

ARM Linux中断机制之中断的初始化

2013-12-19 20:32 344 查看

一,认识几个重要结构体:


1.中断描述符

对于每一条中断线都由一个irq_desc结构来描述。

//在include/linux/irq.h中

struct irq_desc {

unsigned
int irq;//中断号

struct timer_rand_state
*timer_rand_state;

unsigned
int
*kstat_irqs;

#ifdef CONFIG_INTR_REMAP

struct
irq_2_iommu
*irq_2_iommu;

#endif

irq_flow_handler_t handle_irq;//上层中断处理函数,

struct
irq_chip *chip;//底层硬件操作
struct
msi_desc *msi_desc;

void *handler_data;//附加参数,用于handle_irq

void *chip_data;//平台相关附加参数,用于chip

struct
irqaction *action; //中断服务例程链表

unsigned
int status; //中断当前的状态

//中断关闭打开层数调用一次disable_irq( )
,depth加1;调用一次enable_irq( )该值减1,

//如果depth等于0就开启这条中断线,如果depth大于0就关闭中断线。

unsigned
int depth;

unsigned
int wake_depth; ////* 唤醒次数
*/

unsigned
int irq_count; /* 发生的中断次数
*/

unsigned
long last_unhandled;

unsigned
int irqs_unhandled;

spinlock_t lock;

#ifdef CONFIG_SMP

cpumask_var_t affinity;

unsigned
int cpu;

#ifdef CONFIG_GENERIC_PENDING_IRQ

cpumask_var_t pending_mask;

#endif

#endif

atomic_t threads_active;

wait_queue_head_t
wait_for_threads;

#ifdef CONFIG_PROC_FS

struct
proc_dir_entry *dir;///proc/irq/ 入口

#endif

const
char *name;///proc/interrupts
中显示的中断名称

} ____cacheline_internodealigned_in_smp;

2. 中断硬件操作函数集

//在include/linux/irq.h中定义

//该结构体中各函数在文件linux/arch/arm/plat-s3c24xx/irq.c中实现

struct irq_chip {

const
char *name; //用于 /proc/interrupts
unsigned
int (*startup)(unsigned int
irq); //默认为 enable 如果为NULL

void (*shutdown)(unsigned
int irq); //默认为 disable 如果为NULL
void (*enable)(unsigned
int irq); //允许中断,默认为 unmask 如果为NULL

void (*disable)(unsigned
int irq); //禁止中断,默认为 mask 如果为
NULL

void (*ack)(unsigned
int irq); //响应一个中断,清除中断标志

void (*mask)(unsigned
int irq); //mask 一个中断源,通常是关闭中断

void (*mask_ack)(unsigned
int irq); //响应并 mask 中断源

void (*unmask)(unsigned
int irq); //unmask 中断源

void (*eoi)(unsigned
int irq);

void (*end)(unsigned
int irq);

void (*set_affinity)(unsigned
int irq,

const
struct cpumask *dest);

int (*retrigger)(unsigned
int irq);

int (*set_type)(unsigned
int irq, unsigned int flow_type); //设置中断触发方式
IRQ_TYPE_LEVEL

int (*set_wake)(unsigned
int irq, unsigned int on);

#ifdef CONFIG_IRQ_RELEASE_METHOD

void (*release)(unsigned
int irq, void *dev_id);

#endif

const char *typename;

};

3.中断处理例程描述符

//在include/linux/interrupt.h中

struct irqaction {

irq_handler_t
handler;

unsigned long
flags; //用一组标志描述中断线与 I/O 设备之间的关系。

cpumask_t mask;

const char *name;
void
*dev_id;

struct irqaction
*next;

int irq;

struct proc_dir_entry *dir;

irq_handler_t thread_fn;

struct task_struct *thread;

unsigned long thread_flags;

};

这三个结构体间的关系表示如下


Linux中断机制之中断的初始化" />

二,中断初始化过程

中断机制的初始化通过
两个函数完成:early_trap_init()和init_IRQ(),在此我们先讨论函数init_IRQ()。
//函数init_IRQ在文件linux/arch/arm/kernel/irq.c中实现。
void __init
init_IRQ(void)

{

int irq;

for (irq = 0; irq
< NR_IRQS; irq++)

irq_desc[irq].status |=
IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP

cpumask_setall(bad_irq_desc.affinity);

bad_irq_desc.cpu = smp_processor_id();

#endif

init_arch_irq();

}
//函数s3c24xx_init_irq在文件linux/arch/arm/plat-s3c24xx/irq.c中实现
void __init
s3c24xx_init_irq(void)

{

unsigned long pend;

unsigned long last;

int irqno;

int i;

irqdbf("s3c2410_init_irq: clearing interrupt
status flags\n");

last =
0;

for (i = 0; i < 4; i++)
{

pend =
__raw_readl(S3C24XX_EINTPEND);

if (pend == 0 || pend ==
last)

break;

__raw_writel(pend,
S3C24XX_EINTPEND); //清除外部中断寄存器EINTPEND中的请求标志,

printk("irq: clearing pending
ext status x\n", (int)pend);

last = pend;

}
last = 0;

。。。。。。

//设置各中断的底层硬件操作函数集desc->chip,中断上层处理函数desc->handle_irq
for (irqno =
IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++)
{

switch (irqno) {

case IRQ_EINT4t7:

case IRQ_EINT8t23:

case IRQ_UART0:

case IRQ_UART1:

case IRQ_UART2:

case IRQ_ADCPARENT:

set_irq_chip(irqno,
&s3c_irq_level_chip);

set_irq_handler(irqno,
handle_level_irq);

break;

case IRQ_RESERVED6:

case IRQ_RESERVED24:

break;

default:

//irqdbf("registering
irq %d (s3c irq)\n", irqno);

set_irq_chip(irqno,
&s3c_irq_chip);

set_irq_handler(irqno,
handle_edge_irq);

set_irq_flags(irqno,
IRQF_VALID);

}

}
//以下几个中断都有多个中断源,每一个中断源也都有各自的中断号,它们的多个中断源中任意一个产生中断
//该中断都会被触发,而不是直接出发子中断。这几个中断并不处理中断函数,它们的中作是计算子中断的中断号,
//并根据子中断的中断号在数组irq_desc[NR_IRQS]中去找出该中断号对应的irq_desc结构,并调用该结构中的中断处理函数。

set_irq_chained_handler(IRQ_EINT4t7,
s3c_irq_demux_extint4t7);

set_irq_chained_handler(IRQ_EINT8t23,
s3c_irq_demux_extint8);

set_irq_chained_handler(IRQ_UART0,
s3c_irq_demux_uart0);

set_irq_chained_handler(IRQ_UART1,
s3c_irq_demux_uart1);

set_irq_chained_handler(IRQ_UART2,
s3c_irq_demux_uart2);

set_irq_chained_handler(IRQ_ADCPARENT,
s3c_irq_demux_adc);

。。。。。。

for (irqno =
IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {

irqdbf("registering irq %d
(extended s3c irq)\n", irqno);

set_irq_chip(irqno,
&s3c_irqext_chip); //设置子中断的硬件操作函数集

set_irq_handler(irqno,
handle_edge_irq); //设置子中断的上层处理函数

set_irq_flags(irqno,
IRQF_VALID);

}

。。。。。。

}

比如此时外部中断10产生了中断,中断号为IRQ_EINT8t23的中断被触发,执行函数s3c_irq_demux_extint8()。

static void

s3c_irq_demux_extint8(unsigned int irq,

struct irq_desc *desc)

{

unsigned long eintpnd =
__raw_readl(S3C24XX_EINTPEND);

unsigned long eintmsk =
__raw_readl(S3C24XX_EINTMASK);

eintpnd &= ~eintmsk;

eintpnd &=
~0xff;

while (eintpnd) {

irq =
__ffs(eintpnd); //计算该中断在外部中断寄存器EINTPEND中的偏移量

eintpnd &=
~(1<<irq);

irq += (IRQ_EINT4 -
4); //根据这个偏移量重新计算中断号

generic_handle_irq(irq); //根据重新计算的中断号获取对应的结构体irq_desc,并调用它的上层中断处理函数。

}

}

static inline void generic_handle_irq_desc(unsigned int irq, struct
irq_desc *desc)

{

#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ

desc->handle_irq(irq,
desc); //直接调用上层中断处理函数

#else

if
(likely(desc->handle_irq))

desc->handle_irq(irq,
desc);

else

__do_IRQ(irq); //通用中断处理函数,该函数最终调用desc->handle_irq(irq,
desc);

#endif

}

上层中断处理函数

上层中断处理函数有5个分别为:handle_simple_irq(),handle_level_irq(),

handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。

这几个函数在文件kernel/irq/chip.c中实现。常用的有两个handle_level_irq(),和handle_edge_irq()。

这5个上层中断处理函数都是通过调用函数handle_IRQ_event()来做进一步处理。

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction
*action)

{

irqreturn_t ret, retval = IRQ_NONE;

unsigned int status = 0;

if (!(action->flags
& IRQF_DISABLED))

local_irq_enable_in_hardirq();

do {

。。。。。。

ret =
action->handler(irq,
action->dev_id); //执行中断处理函数。

。。。。。。

retval |= ret;

action =
action->next;

} while (action); //调用该中断线上的所有例程

if (status &
IRQF_SAMPLE_RANDOM)

add_interrupt_randomness(irq);

local_irq_disable();

return retval;

}

void

handle_level_irq(unsigned int irq, struct irq_desc *desc)

{

struct irqaction *action;

irqreturn_t action_ret;

。。。。。。

desc = irq_remap_to_desc(irq, desc);

。。。。。。

action =
desc->action;

action_ret =
handle_IRQ_event(irq, action);

。。。。。。

}

void

handle_edge_irq(unsigned int irq, struct irq_desc *desc)

{

spin_lock(&desc->lock);

。。。。。。

desc = irq_remap_to_desc(irq,
desc);

。。。。。。

desc->status |=
IRQ_INPROGRESS;

do {

struct irqaction *action =
desc->action;

。。。。。。

desc->status
&= ~IRQ_PENDING;

spin_unlock(&desc->lock);

action_ret =
handle_IRQ_event(irq, action);

if (!noirqdebug)

note_interrupt(irq,
desc, action_ret);

spin_lock(&desc->lock);

//该函数与函数handle_level_irq不太一样的是,该函数多了一个循环。即如果在本次中断

//的处理过程中该中断线上又有中断产生,则再次执行该中断线上的处理例程

#define IRQ_DISABLED 2

#define
IRQ_PENDING 4

#define IRQ_REPLAY 8

#define IRQ_WAITING 32

*/

} while ((desc->status
& (IRQ_PENDING | IRQ_DISABLED)) ==
IRQ_PENDING);

desc->status &=
~IRQ_INPROGRESS;

out_unlock:

spin_unlock(&desc->lock);

}

评论这张


Linux中断机制之中断的初始化" TITLE="ARM Linux中断机制之中断的初始化" />转发至微博
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: