linux do_irq 报错 代码,linux-2.6.38中断机制分析—asm_do_IRQ

linux-2.6.38中断机制分析—asm_do_IRQ

注释:用于显示的原因 分别用/@ @/替换

中断的处理过程

asm_do_IRQ是中断的C语言总入口函数,它在/arch/arm/kernel/irq.c中定义,

106 asmlinkage void __exception

asm_do_IRQ(unsigned int irq, struct pt_regs

*regs)

107 {

108 struct pt_regs *old_regs =

set_irq_regs(regs);

109

110 irq_enter();

111

112 /@

113 * Some hardware gives randomly

wrong interrupts. Rather

114 * than crashing, do something

sensible.

115 @/

116 if (unlikely(irq

>= NR_IRQS)) {

117 if

(printk_ratelimit())

118 printk(KERN_WARNING "Bad

IRQ%u\n", irq);

119

ack_bad_irq(irq);

120 } else {

121

generic_handle_irq(irq);

122 }

124 /@ AT91 specific workaround

@/

125

irq_finish(irq);

127 irq_exit();

128

set_irq_regs(old_regs);

129 }

第121行的通过generic_handle_irq(irq)就可以调用desc结构中的handle_irq成员函数,也就是Irq_desc[irq].handle_irq.在Linux/include/linux/irq.h,可以看到其声明

308 static inline void

generic_handle_irq_desc(unsigned int irq, struct irq_desc

*desc)

309 {

310 #ifdef

CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ

311

desc->handle_irq(irq, desc);

312 #else

313 if

(likely(desc->handle_irq))

314

desc->handle_irq(irq, desc);

315 else

316 __do_IRQ(irq);

317 #endif

318 }

319

320 static inline void

generic_handle_irq(unsigned int irq)

321 {

322 generic_handle_irq_desc(irq,

irq_to_desc(irq));

323 }

asm_do_IRQ函数中参数irq的取值范围为IRQ_EINT0~(IRQ_EINT0

+ 31),只有32个取值。它可能是一个际的中断号,也可能是一组中断的中断号。这里有S3C2440的芯片特性决定的:发生中断时,INTPND寄存器的某一位被置1,INTOFFSET寄存器中记录了是哪一位(0--31),中断向量调用asm_do_IRQ之前要把INTOFFSET寄存器的值确定irq参数。每一个实际的中断在irq_desc数组中都有一项与它对应,它们的数目不止32.当asm_do_IRQ函数参数irq表示的是“一组”中断时,irq_desc[irq].handle_irq成员函数还需要先分辨出是哪一个中断,然后调用irq_desc[irqno].handle_irq来进一步处理。以外部中断EINT8—EINT23为例,它们通常是边沿触发

(1) 它们被触发里,INTOFFSET寄存器中的值都是5,asm_do_IRQ函数中参数irq的值为(IRQ_EINTO+5),即IRQ_EINT8t23,

(2)irq_desc[IRQ_EINT8t23].handle_irq在前面init_IRQ函数初始化中断体系结构的时候被设为s3c_irq_demux_extint8.

(3)s3c_irq_demux_extint8函数的代码在arch/arm/plat-s3c24xx/irq.c中,它首先读取EINTPEND、EINTMASK寄存器,确定发生了哪些中断,重新计算它们的中断号,然后调用irq_desc数组项中handle_irq成员函数

(4)IRQ_EINT8--IRQ_EINT23这几个中断的处理函数入口,在init_IRQ函数初始化中断体系结构的时候已经被设置为handle_edge_irq函数,desc_handle_irq(irq,irq_desc+irq)就是调用这个函数,它在kernel/irq/chip.c中定义,它用来处理边沿触发的中断,在Linux/arch/arm/mach-s3c2410/include/mach/irqs.h中,可以看到下面的一段代码

30 #define IRQ_EINT4t7

S3C2410_IRQ(4) /@ 20 @/

31 #define

IRQ_EINT8t23 S3C2410_IRQ(5)

32 #define

IRQ_RESERVED6 S3C2410_IRQ(6) /@ for s3c2410 @/

s3c_irq_demux_extint8()函数

453 s3c_irq_demux_extint8(unsigned

int irq,

454 struct irq_desc

*desc)

455 {

456 unsigned long eintpnd =

__raw_readl(S3C24XX_EINTPEND); //EINT8-EINT23 发生时,相应位被置1

457 unsigned long eintmsk =

__raw_readl(S3C24XX_EINTMASK);//屏蔽寄存器

459 eintpnd &=

~eintmsk; //清除被屏蔽的位

460 eintpnd &=

~0xff; /@ 清除低8位(EINT8对应位8)ignore

lower irqs @/

462 /@ 循环处理所有子中断@/

464 while (eintpnd)

{

465 irq = __ffs(eintpnd);

//确定eintpnd中为1的最高位

466 eintpnd &=

~(1<

468 irq += (IRQ_EINT4 -

4);//重新计算中断号,前面计算出irq等于8时,中断号为IRQ_EINT8

469

generic_handle_irq(irq);//调用这中断的真正的处理函数

472 }

(4)IRQ_EINT8--IRQ_EINT23这几个中断的处理函数入口,在init_IRQ函数初始化中断体系结构的时候已经被设置为handle_edge_irq函数,desc_handle_irq(irq,irq_desc+irq)就是调用这个函数,它在kernel/irq/chip.c中定义,它用来处理边沿触发的中断,

void

531 handle_edge_irq(unsigned int

irq, struct irq_desc *desc)

532 {

535 desc->status

&= ~(IRQ_REPLAY | IRQ_WAITING);

............

551 if

(desc->chip->ack)

552

desc->chip->ack(irq);

............

580 action_ret =

handle_IRQ_event(irq, action);

586 .........

587 desc->status

&= ~IRQ_INPROGRESS;

590 }

552行响应中断,通常是清除当前中断使得可以接收下一个中断,对于IRQ_EINT8~IRQ_EINT23这几个中断,

desc->chip在前面init_IRQ函数初始化中断体系结构的时候被设为s3c_irqext_chip.desc->chip->ack就是s3c_irqext_ack函数,(arch/armplat-s3c24xx/irq.c)它用来清除中断。在第580行handle_IRQ_event函数来逐个查找action链表中跟中断号匹配的中断处理函数,然后执行用户注册的中断处理函数,它在kernel/irq/handle.c中定义。关键代码如下:

370 irqreturn_t

handle_IRQ_event(unsigned int irq, struct irqaction

*action)

371 {

.............

378 do {

379 trace_irq_handler_entry(irq,

action);

380 ret =

action->handler(irq,

action->dev_id);//执行用户注册的中断处理函数

381 trace_irq_handler_exit(irq,

action, ret);

382

383 switch (ret) {

384 case

IRQ_WAKE_THREAD:

389 ret =

IRQ_HANDLED;

395 if

(unlikely(!action->thread_fn)) {

396 warn_no_thread(irq,

action);

397 break;

398 }

408 if

(likely(!test_bit(IRQTF_DIED,

409

&action->thread_flags)))

{

410 set_bit(IRQTF_RUNTHREAD,

&action->thread_flags);

411

wake_up_process(action->thread);

412 }

415 case

IRQ_HANDLED:

416 status |=

action->flags;

417 break;

418

419 default:

420 break;

421 }

422

423 retval |= ret;

424 action =

action->next; //下一个

425 } while

(action);

.......

432 }

从380行可知用户注册的中断处理函数的参数为中断号irq,action->dev_id。后一个参数是通过request_irq函数注册中断时传入的dev_id参数,它由用户自己指定、自己使用,可以为空,当这个中断是“共享中断”时除外

对于电平触发的中断,它们的irq_desc[irq].handle_irq通常是handle_level_irq函数。它也是在

kernel/irq/chip.c中定义,其功能与上述handle_edge_irq函数相似,

handle_level_irq(unsigned int irq,

struct irq_desc *desc)

423 {

424 struct irqaction

*action;

425 irqreturn_t

action_ret;

426

427

spin_lock(&desc->lock);

428 mask_ack_irq(desc,

irq);

429

430 if

(unlikely(desc->status &

IRQ_INPROGRESS))

431 goto

out_unlock;

432 desc->status

&= ~(IRQ_REPLAY | IRQ_WAITING);

433 kstat_incr_irqs_this_cpu(irq,

desc);

434

435 /@

436 * If its disabled or no action

available

437 * keep it masked and get out of

here

438 @/

439 action =

desc->action;

440 if (unlikely(!action ||

(desc->status &

IRQ_DISABLED)))

441 goto

out_unlock;

442

443 desc->status |=

IRQ_INPROGRESS;

444

spin_unlock(&desc->lock);

445

446 action_ret =

handle_IRQ_event(irq, action);

447 if

(!noirqdebug)

448 note_interrupt(irq, desc,

action_ret);

449

450

spin_lock(&desc->lock);

451 desc->status

&= ~IRQ_INPROGRESS;

452

453 if

(unlikely(desc->status &

IRQ_ONESHOT))

454 desc->status |=

IRQ_MASKED;

455 else if

(!(desc->status & IRQ_DISABLED)

&&

desc->chip->unmask)

456

desc->chip->unmask(irq);

457 out_unlock:

458

spin_unlock(&desc->lock);

459 }

对于handle_level_irq函数已经清除了中断,但是它只限于清除SoC内部的的信号,如果外设输入到SoC的中断信号仍然有效,这就会导致当前中断处理完成后,会误认为再次发生了中断,对于这种情况,需要用户注册的中断处理函数中清除中断,先清除外设的中断,然后再清除SoC内部的中断号。

中断的处理流程可以总结如下:

(1)中断向量调用总入口函数asm_do_IRQ,传入根据中断号irq

(2)asm_do_IRQ函数根据中断号irq调用irq_desc[irq].handle_irq,它是这个中断的处理函数入口,对于电平触发的中断,这个入口函数通常为handle_level_irq,对于边沿触发的中断,这个入口通常为handle_edge_irq

(3)入口函数首先清除中断,入口函数是handle_level_irq时还要屏蔽中断

(4)逐个调用用户在irq_desc[irq].aciton链表中注册的中断处理函数

(5) 入口函数是handle_level_irq时,在注册的中断处理程序中,还要先清除外设的中断,然后再清除SoC内部的中断号。

卸载中断处理函数这通过free_irq函数来实现,它与request_irq一样,也是在kernel/irq/mangage.c中定义:

它需要用到两个参数:irq和dev_id,它们与通过request_irq注册中断函数时使用的参数一样,使用中断号irq定位action链表,再使用dev_id在action链表中找到要卸载的表项。同一个中断的不同中断处理函数必须使用不同的dev_id来区分,在注册共享中断时参数dev_id必惟一。free_irq函数的处理过程与request_irq函数相反

(1)根据中断号irq,dev_id从action链表中找到表项,将它移除

(2)如果它是惟一的表项,还要调用IRQ_DESC[IRQ].CHIP->SHUTDOWN

或IRQ_DESC[IRQ].CHIP->DISABLW来关闭中断

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值