现在的位置: 首页 > 综合 > 正文

ARM中断及其处理

2014年01月05日 ⁄ 综合 ⁄ 共 3506字 ⁄ 字号 评论关闭

1,关于硬件部分的介绍。

1.1ARM920T的中断

两种中断模式:FIQ,IRQ。

1.2几个寄存器

SRCPND:请求中断的中断源。可以有多个位被置为1,可读可写,用完清0。只对irq模式有效。INTPND:当前正在执行的中断服务程序。只有一个为被置为1,可读可写,用完清0只对irq模式有效。INTMOD:某一位置1,则该位的中断源被设置为FIQ模式,否则为IRQ模式。INTMSK:某位为1,则该 位的中断被忽略INTOFFSET:The
value in the interrupt offset register shows which interrupt request of IRQ mode is in the INTPND register.SUBSRCPND:比如SRCPND[9]叫INT_WDT_AC97,意思是说WatchDogTimer或者AC97有中断,到底是那个,还得看SUBSRCPND[13:14],如果13那就是WDT,如果是   14那就是AC97。INTSUBMSK:SUB寄存器的存在可以理解为“共享中断设置寄存器(INTMSK等等)”的“详细说明”,就是指明共享中断包括了哪些东西。

EXTINT0/1/2:接下来的这三个寄存器用于设置外部中断的触发方式。EINTMASK:某位写1,则对应的位中断屏蔽EINTPEND:中断发生,则相应的为置1

1.3硬件部分的小结

2 中断过程以及linux对中断的处理过程

2.1 一些设置

2.1.1中断向量表在哪里

硬件发生中断后,ARM架构cpu的异常向量基址可以是0x00000000,也可以是0xffff0000,linux内核使用的是后者。看到这里的时候,遇到一个问题。为什么是这两个地址,按照它字面的意思,应该是这两个地址可以根据硬件来选择的。那么如果是这样,在哪儿选,怎么选?(1)通过查看数据手册,s3c2440有效的映射空间是1G(0x40000000),显然,0xffff0000已经超过了1G的范围,那么可以肯定,0xffff0000是一个虚拟地址。那么如果是虚拟地址,是怎么实现的?还有一个问题不容忽略,对于硬件而言,发生中断后PC指针肯定是跳到物理地址的0x0地址,也就是说,无论是虚拟地址0x00000000还是0xffff0000,最后其实都是映射到物理地址的0x00000000。当然,在这之间还有个小问题没有解决,到底linux是通过什么来确定到底使用哪一个地址的?答案如下:ARMv4以下的版本,该地址固定为0;ARMv4及以上版本,ARM中断向量表的地址由CP15协处理器c1寄存器中V位(bit[13])控制,V和中断向量表的对应关系如下:V=0
   ~    0x00000000~0x0000001CV=1    ~    0xffff0000~0xffff001C从这里可以看出,前面犯了一个错误,即中断发生时,PC指针不一定跳到0x00000000。现在就剩下一个问题了,0xffff0000这个地址,是怎么样和linux的中断联系起来的。(2)事实上,在通过对CP15协处理器c1bit【13】进行操作之后,硬件接收到中断时,可以是跳转到0xffff0000这个地址,这个地址并不存在存储介质,所以我们需要对这个“虚拟地址”进行映射(这里特别要注意,“映射”和虚拟内存中的“查表”是不一样的),详细的在:http://tanatseng.blog.163.com/blog/static/174991629201121801359435/。这样一来,PC跳到0xffff0000这个地址以后,发现自己其实是在物理内存的某一个地址上,而这个地址接下来的1C个字节中,存储了中断向量表。中断向量表的位置就是这么来的。

2.1.2中断向量表的建立

在确定了中断向量表的位置之后,后面的事情就好办了,无非是向量表以及函数的一些copy动作。原理比较简单。不多说了。

2.1.3小结

前面简单介绍了一些东西,与用户的编程关系不大。由操作系统或者硬件完成。了解这些,有助于后面一些复杂概念的理解。

2.2 操作系统都干了些什么以及用户该怎么做

2.2.1 初始化部分

linux通过一个isr_desc结构体数组来描述中断:每个数组项对应一个中断。

  1. struct irp_desc {  
  2.   
  3.   
  4. irp_flow_handler_t    handle_irq;//当前中断的处理函数入口  
  5.   
  6.   
  7. struct irq_chip          *chip;//低层的硬件访问  
  8.   
  9.   
  10. struct irqaction         *action;//用户提供的中断处理函数链表  
  11.   
  12.   
  13. unsigned int             status;//irq状态  
  14.   
  15.   
  16. const char               *name;//中断名称  
  17.   
  18.   
  19. }___cacheline_internodealinged_in_smp;  

中断处理的过程大致是这样的:

(1)发生中断时,CPU执行异常向量vector_irq的代码
(2)在vector_irq里面,最终会调用中断处理的总入口函数asm_do_IRQ。
(3)asm_do_IRQ根据中断号调用irq_desc数组项中的hand_irq。
(4)hand_irq会使用chip成员中的函数来设置硬件,比如清楚中断,禁止中断,重新使能中断等。
(5)handle_irq逐个调用用户在action链表中注册的处理函数。

以上都很好理解,整个初始化部分,简单理解无非就是三个结构体 的初始化。对于用户而言,需要关心(5),如何在action链表中注册中断处理函数。

2.2.2 action链表中注册中断处理函数

对于用户,注册部分只是调用一个函数就已经结束了,剩下的内核会自动完成。

2.2.3 几个问题

到此,还有几个小小的问题。第一,在request_irq这个函数中的参数 irq,取值范围是多少,32还是60?s3c2440发生中断时,可能是一个或者一组中断的中断号,那么这个irq是指具体的中断还是某一类中断呢?第二,之前提到,成员函数action是一个链表,中断在处理服务子程序时会一次调用action链表中的各个函数,在2.2.2的开始我们只是将一个函数注册进入内核,如果有多个中断服务子程序呢?对于一,irq的取值范围是0-31.也就是说注册函数只是想内核说明中断时哪一种类型,具体是哪一个中断,是内核调用asm_do_IRQ,然后再由asm_do_IRQ调用irq_decs.handle来计算的。计算完成之后,假设计算的中断号为INTno,那么再调用irq_decs[INTno].handle来做具体的中断服务。

  1. int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *devid);  

对于二,其实只要多次调用request_irq这个函数即可。分两种情况:

①如果action链表为空,则直接链入

②否则先判断新建的irqaction结构和链表中的irqaction结构所表示的中断类型是否一致;即是否都申明为“可共享的”(IRQF_SHARED)、是否都使用形同的触发方式(电平、边沿、极性),如果一致,则将新建的irqaction结构链入。

这两段话中提到一个概念共享中断。网上查到大致是这么来说明的:

例如有A和B两个设备,公用了CPU的一根外部中断线INT0.当设置为共享中断时,A设备发生了中断,B设备产生的中断依然可以被CPU识别。设置为非共享中断时,则A设备发生中断时,不再接收B设备的中断信号。

问题又来了,对于上述的这个情况,如果是共享中断,CPU如何进行判断,哪一个设备对应哪一个中断呢??

这里涉及到request_irq中dev_id这个参数的应用,大致有两个作用,

一.在中断处理程序释放时使用到dev_id

二.将使用该中断处理程序的设备结构体传递给该中断处理程序

具体在这里:http://www.cublog.cn/u2/60011/showart_1086513.html

2.3卸载中断处理函数

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

抱歉!评论已关闭.