02.07.2013 Views

kernel 里面的中断代码分析

kernel 里面的中断代码分析

kernel 里面的中断代码分析

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Index :<br />

1.1 涉及到的代码简单分析<br />

<strong>kernel</strong> <strong>里面的中断代码分析</strong><br />

1.2 中断处理函数是如何被 called ?(转载的,参考,了解流程即可,具体代<br />

码不必深究)<br />

1.3 s3c2410 里面的中断控制器的细节分析,以及 general 的中断原理<br />

1.4 外部中断控制器的分析(与GPIO有关)<br />

1.1 涉及到的代码简单分析:<br />

大致分析:<br />

以前看 IRQ 的部分, 都没有看到一个部分就是跟 chip 有关的代码。<br />

通过看 request_irq () 还有 setup_irq() 都跟具体的 chip 有关的。<br />

这个跟 chip 有关的代码可以到 arch/arm/mach-smdk2410/irq.c 里面查询。<br />

比如: 跟具体的 cpu 有关的:<br />

for (irqno = IRQ_EINT0; irqno


}<br />

还要注意 <strong>kernel</strong>/irq/chip.c 里面的函数。<br />

比如 do_level_IRQ() 函数,<br />

具体的大家可以分析 handle_level_irq() 和 handle_edge_irq()<br />

硬件中断芯片描述苻<br />

/**<br />

* struct irq_chip - hardware interrupt chip descriptor<br />

*<br />

* @name: name for /proc/interrupts<br />

* @startup: start up the interrupt (defaults to ->enable if NULL)<br />

* @shutdown: shut down the interrupt (defaults to ->disable if NULL)<br />

* @enable: enable the interrupt (defaults to chip->unmask if NULL)<br />

* @disable: disable the interrupt (defaults to chip->mask if NULL)<br />

* @ack: start of a new interrupt<br />

* @mask: mask an interrupt source<br />

* @mask_ack: ack and mask an interrupt source<br />

* @unmask: unmask an interrupt source<br />

* @eoi: end of interrupt - chip level<br />

* @end: end of interrupt - flow level<br />

* @set_affinity: set the CPU affinity on SMP machines<br />

* @retrigger: resend an IRQ to the CPU<br />

* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ<br />

* @set_wake: enable/disable power-management wake-on of an IRQ<br />

*<br />

* @release: release function solely used by UML<br />

* @typename: obsoleted by name, kept as migration helper<br />

*/<br />

struct irq_chip {<br />

。。。<br />

}<br />

对于所有的 cpu 和平台来讲, 通用的 “中断描述符表”<br />

它记录了每个 irq 的所有信息, 包括 irq_handler ,status<br />

/**


* struct irq_desc - interrupt descriptor<br />

*<br />

* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]<br />

* @chip: low level interrupt hardware access<br />

* @handler_data: per-IRQ data for the irq_chip methods<br />

* @chip_data: platform-specific per-chip private data for the chip<br />

* methods, to allow shared chip implementations<br />

* @action: the irq action chain<br />

* @status: status information<br />

* @depth: disable-depth, for nested irq_disable() calls<br />

* @wake_depth: enable depth, for multiple set_irq_wake() callers<br />

* @irq_count: stats field to detect stalled irqs<br />

* @irqs_unhandled: stats field for spurious unhandled interrupts<br />

* @lock: locking for SMP<br />

* @affinity: IRQ affinity on SMP<br />

* @cpu: cpu index useful for balancing<br />

* @pending_mask: pending rebalanced interrupts<br />

* @move_irq: need to re-target IRQ destination<br />

* @dir: /proc/irq/ procfs entry<br />

* @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP<br />

*<br />

* Pad this out to 32 bytes for cache and indexing reasons.<br />

*/<br />

struct irq_desc {<br />

void fastcall (*handle_irq)(unsigned int irq,<br />

struct irq_chip *chip;<br />

void *handler_data;<br />

void *chip_data;<br />

struct irq_desc *desc,<br />

struct pt_regs *regs);<br />

struct irqaction *action; /* IRQ action list */<br />

unsigned int status; /* IRQ status */<br />

unsigned int depth; /* nested irq disables */<br />

unsigned int wake_depth; /* nested wake enables */<br />

unsigned int irq_count; /* For detecting broken IRQs */<br />

unsigned int irqs_unhandled;<br />

spinlock_t lock;<br />

#ifdef CONFIG_SMP<br />

#endif<br />

cpumask_t affinity;<br />

unsigned int cpu;<br />

#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)<br />

cpumask_t pending_mask;


#endif<br />

unsigned int move_irq; /* need to re-target IRQ dest */<br />

#ifdef CONFIG_PROC_FS<br />

#endif<br />

struct proc_dir_entry *dir;<br />

} ____cacheline_aligned;<br />

重点分析 request_irq() 函数和 setup_irq() 函数 跟移植有关系的部分 。<br />

Kernel/irq/manage.c =>request_irq()<br />

int request_irq(unsigned int irq,<br />

{<br />

irqreturn_t (*handler)(int, void *, struct pt_regs *),<br />

unsigned long irqflags, const char *devname, void *dev_id)<br />

struct irqaction *action;<br />

int retval;<br />

#ifdef CONFIG_LOCKDEP<br />

/*<br />

#endif<br />

* Lockdep wants atomic interrupt handlers:<br />

*/<br />

irqflags |= SA_INTERRUPT;<br />

/*<br />

* Sanity-check: shared interrupts must pass in a real dev-ID,<br />

* otherwise we'll have trouble later trying to figure out<br />

* which interrupt is which (messes up the interrupt freeing<br />

* logic etc).<br />

*/<br />

if ((irqflags & IRQF_SHARED) && !dev_id)<br />

return -EINVAL;<br />

if (irq >= NR_IRQS)<br />

return -EINVAL;<br />

if (irq_desc[irq].status & IRQ_NOREQUEST) //这个要初始化,否则无法 request_irq , 显然这里应该在 <strong>kernel</strong> 启动的<br />

过程调用 irq 相关的函数进行初始化<br />

return -EINVAL;<br />

if (!handler)<br />

return -EINVAL;<br />

action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);


}<br />

if (!action)<br />

return -ENOMEM;<br />

action->handler = handler;<br />

action->flags = irqflags;<br />

cpus_clear(action->mask);<br />

action->name = devname;<br />

action->next = NULL;<br />

action->dev_id = dev_id;<br />

select_smp_affinity(irq);<br />

retval = setup_irq(irq, action);<br />

if (retval)<br />

kfree(action);<br />

return retval;<br />

EXPORT_SYMBOL(request_irq);<br />

Kernel/irq/manage.c setup_irq()<br />

/* 内部函数来注册 irqaction<br />

/*<br />

* 典型的用于分配给特定的中断,这些中断体系结构的一部分<br />

* 比如像 2410 手册里面定义的中断那样<br />

* Internal function to register an irqaction - typically used to<br />

* allocate special interrupts that are part of the architecture.<br />

*/<br />

/* 共享中断的 irq , 应该有多个 irqaction<br />

* 我们要做的就是在 irqaction 的链表的末尾<br />

* 填上我们自己的 handler<br />

*/<br />

int setup_irq(unsigned int irq, struct irqaction *new)<br />

{<br />

struct irq_desc *desc = irq_desc + irq;<br />

struct irqaction *old, **p;<br />

unsigned long flags;<br />

int shared = 0;


if (irq >= NR_IRQS)<br />

return -EINVAL;<br />

if (desc->chip == &no_irq_chip) //这里很很关键<br />

/*<br />

return -ENOSYS; //功能没有实现<br />

* Some drivers like serial.c use request_irq() heavily,<br />

* so we have to be careful not to interfere with a<br />

* running system.<br />

*/<br />

if (new->flags & IRQF_SAMPLE_RANDOM) {<br />

}<br />

/*<br />

/*<br />

* This function might sleep, we want to call it first,<br />

* outside of the atomic block.<br />

* Yes, this might clear the entropy pool if the wrong<br />

* driver is attempted to be loaded, without actually<br />

* installing a new handler, but is this really a problem,<br />

* only the sysadmin is able to do this.<br />

*/<br />

rand_initialize_irq(irq);<br />

* The following block of code has to be executed atomically<br />

*/<br />

spin_lock_irqsave(&desc->lock, flags);<br />

// ----------------------------------------------------------------------------------------------------<br />

p = &desc->action;<br />

old = *p;<br />

if (old) {<br />

/*<br />

* Can't share interrupts unless both agree to and are<br />

* the same type (level, edge, polarity). So both flag<br />

* fields must have IRQF_SHARED set and the bits which<br />

* set the trigger type must match.<br />

*/<br />

if (!((old->flags & new->flags) & IRQF_SHARED) ||<br />

((old->flags ^ new->flags) & IRQF_TRIGGER_MASK))<br />

goto mismatch;


#if defined(CONFIG_IRQ_PER_CPU)<br />

#endif<br />

}<br />

/* All handlers must agree on per-cpuness */<br />

if ((old->flags & IRQF_PERCPU) !=<br />

(new->flags & IRQF_PERCPU))<br />

goto mismatch;<br />

/* add new interrupt at end of irq queue */<br />

do {<br />

p = &old->next;<br />

old = *p;<br />

} while (old);<br />

shared = 1;<br />

//-----------------------------------------------------------------------------------------------------------------------<br />

*p = new;<br />

#if defined(CONFIG_IRQ_PER_CPU)<br />

#endif<br />

if (new->flags & IRQF_PERCPU)<br />

desc->status |= IRQ_PER_CPU;<br />

//这边还得设置 chip 级别的设置,看来我们在 porting <strong>kernel</strong> 的时候,可能还要加<br />

这些函数<br />

if (!shared) {<br />

irq_chip_set_defaults(desc->chip);<br />

/* Setup the type (level, edge polarity) if configured: */<br />

if (new->flags & IRQF_TRIGGER_MASK) {<br />

if (desc->chip && desc->chip->set_type)<br />

else<br />

desc->chip->set_type(irq,<br />

/*<br />

new->flags & IRQF_TRIGGER_MASK);<br />

* IRQF_TRIGGER_* but the PIC does not support<br />

* multiple flow-types?<br />

*/<br />

printk(KERN_WARNING "No IRQF_TRIGGER set_type "<br />

"function for IRQ %d (%s)\n", irq,


} else<br />

desc->chip ? desc->chip->name :<br />

"unknown");<br />

compat_irq_chip_set_default_handler(desc);<br />

//这些标志都 clear ,都跟具体的 irq 有关<br />

}<br />

desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);<br />

if (!(desc->status & IRQ_NOAUTOEN)) {<br />

} else<br />

desc->depth = 0;<br />

desc->status &= ~IRQ_DISABLED;<br />

if (desc->chip->startup)<br />

else<br />

desc->chip->startup(irq);<br />

desc->chip->enable(irq);<br />

/* Undo nested disables: */<br />

desc->depth = 1;<br />

spin_unlock_irqrestore(&desc->lock, flags);<br />

new->irq = irq;<br />

register_irq_proc(irq);<br />

new->dir = NULL;<br />

register_handler_proc(irq, new);<br />

return 0;<br />

mismatch:<br />

}<br />

spin_unlock_irqrestore(&desc->lock, flags);<br />

if (!(new->flags & IRQF_PROBE_SHARED)) {<br />

}<br />

printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);<br />

dump_stack();<br />

return -EBUSY;<br />

当某个外部硬件触发中断的时候, <strong>kernel</strong> 最终会调用到<br />

Arch/arm/<strong>kernel</strong>/irq.c asm_do_IRQ()


*<br />

* do_IRQ handles all hardware IRQ's. Decoded IRQs should not<br />

* come via this function. Instead, they should provide their<br />

* own 'handler'<br />

*/<br />

asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)<br />

{<br />

}<br />

struct irqdesc *desc = irq_desc + irq;<br />

/*<br />

* Some hardware gives randomly wrong interrupts. Rather<br />

* than crashing, do something sensible.<br />

*/<br />

if (irq >= NR_IRQS)<br />

desc = &bad_irq_desc;<br />

irq_enter();<br />

desc_handle_irq(irq, desc, regs);<br />

/* AT91 specific workaround */<br />

irq_finish(irq);<br />

irq_exit();<br />

<strong>kernel</strong> 启动过程中的 IRQ 的初始化 :<br />

start_<strong>kernel</strong> ==>init_IRQ()<br />

void __init init_IRQ(void)<br />

{<br />

/*<br />

int irq;<br />

request_irq():<br />

if (irq_desc[irq].status & IRQ_NOREQUEST) //这个要初始化,否则无法 request_irq , 显然这里应该在 <strong>kernel</strong> 启动的过程<br />

调用 irq 相关的函数进行初始化<br />

*/<br />

return -EINVAL;<br />

for (irq = 0; irq < NR_IRQS; irq++)<br />

irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE | IRQ_NOPROBE;


#ifdef CONFIG_SMP<br />

#endif<br />

}<br />

bad_irq_desc.affinity = CPU_MASK_ALL;<br />

bad_irq_desc.cpu = smp_processor_id();<br />

init_arch_irq(); //mach-smdk2410.c ==> .init_irq = s3c24xx_init_irq, //irq.c<br />

1.2 中断处理函数是如何被 called ?<br />

[自己的分析 bob]<br />

中断首先是一个硬件行为, 而处理中断呢, 显然又是一个软件行为,作为 driver writer only 实现中断处理函数, 并与中<br />

断号进行关联也就 ok 了。那么就引出一个问题? 当硬件触发中断的时候,怎么调用到中断处理函数的呢?<br />

要搞清楚这个问题,就要搞清楚,发生中断的时候 CPU 做了什么<br />

以 arm 为例,当产生 IRQ 请求的时候, CPU 做了什么:<br />

1> 处理器切换到特定的中断请求模式(IRQ 模式),表明产生了中断。<br />

2> 前一个模式的 cpsr 被保存到新的模式下的 spsr。<br />

3> PC 指针被保存到新的模式的 lr 寄存器<br />

4> 关中断----在 cpsr 中禁止 I-bit ,会立即阻止其他的 IRQ 产生。<br />

5> 处理器跳转到异常向量表中相应的入口(对于 IRQ , 显然 pc=0x18) 。<br />

说道这里,就涉及到了异常向量表,这个异常向量表就是软件和 CPU 的接口。 CPU 知道一个 source 触<br />

发了中断,怎么调用执行一些函数(汇编,或者 c 语言),就是靠异常向量表(事实上,exception vector table 也是由汇编<br />

组成的)<br />

异常 模式 向量表偏移<br />

复位(reset) SVC +0x00<br />

未定义指令 UND +0x04<br />

软件中断(SWI) SVC +0x08<br />

欲取指终止 ABT +0x0c<br />

数据终止 ABT +0x10<br />

未分配 -- +0x14<br />

IRQ IRQ +0x18<br />

FIQ FIQ +0x1c<br />

表 arm 异常,对应模式及向量表偏移(摘自 arm 体系结构与编程一书)


从这表中, 我们很清楚了, 当触发 IRQ 的时候, CPU 会最后跳入 0x18 这个入口 , 那我们做 <strong>kernel</strong> 的人, 只需在这<br />

个入口填入自己的指令(当然是汇编语句) , 来调用 中断处理函数,可能这样。<br />

中断- cpu jump 到 0x18 ,同时要把 irqno 传入相应的寄存器 调用一个中断通用处理函数比如叫<br />

asm_do_IRQ(unsigned int irqno) asm_do_IRQ() 这个函数根据 irqno 就可以找到对应的中断描述符 然后调用中断<br />

描述符里面的 handler() 了 。<br />

那具体的 <strong>kernel</strong> 里面的细节是什么样的呢? 具体看下面这篇转载的文章即可。代码分析的就比较透彻了, 事实上我们只<br />

要上面说的流程,对于 driver writer 已经足够了。<br />

【转载】<br />

那么 当硬件触发中断的时候, <strong>kernel</strong> 里面的 asm_do_IRQ() 是如何被调用到呢?<br />

遭到一篇很好的文章:<br />

http://hi.baidu.com/wudx05/blog/item/5314935c834f4e41fbf2c0dc.html<br />

现摘录如下:<br />

Armor 的中断向量表放在内存的什么位置?<br />

中断向量表放在 arch/arm/<strong>kernel</strong>/entry-armv.S 这个文件里<br />

__vectors_start:<br />

#swi SYS_ERROR0<br />

b vector_addrexcptn + stubs_offset<br />

b vector_und + stubs_offset<br />

ldr pc, .LCvswi + stubs_offset<br />

b vector_pabt + stubs_offset<br />

b vector_dabt + stubs_offset<br />

b vector_addrexcptn + stubs_offset<br />

b vector_irq + stubs_offset<br />

b vector_fiq + stubs_offset<br />

.globl __vectors_end<br />

__vectors_end:<br />

在 arch/arm/traps.c 中


memcpy((void *)0xffff0000, __vectors_start, __vectors_end - __vectors_start); //中断向<br />

量表被拷贝到 0xffff0000 处,硬件规定的位置<br />

memcpy((void *)0xffff0200, __stubs_start, __stubs_end - __stubs_start);<br />

硬件发生中断后怎样找到中断入口函数的?<br />

从上面的中断向量表中可 以看到,发生中断时系统会跳到 vector_irq + stubs_offset 处运行,这个位置<br />

实际上就是中断入口函数。vector_irq 已经是中断的入口函数了,为什么又要加上 stubs_offset?是因为<br />

b 指令实际上是相对相前 PC 的跳转,也就是说当汇编器看到 B 指令后会把要跳转的标签转化为相对于当前<br />

PC 的偏移量写入指 令码。从上面的代码可以看到中断向量表和 stubs 都发生了代码搬移,所以如果中断向<br />

量表中仍然写成 b vector_irq,那么实际执行的时候就无法跳转到搬移后的 vector_irq 处,因为指令码里<br />

写的是原来的偏移量,所以需要把指令码中的偏移量写 成搬移后的。我们把搬移前的中断向量表中的 irq<br />

入口地址记 irq_PC,它在中断向量表的偏移量就是 irq_PC-vectors_start, vector_irq 在 stubs 中的偏移<br />

量是 vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start 在 0xffff0000<br />

处,而 stubs_start 在 0xffff0200 处,所以搬移后的 vector_irq 相对于中断 向量中的中断入口地址的偏<br />

移量就是,200+vector_irq 在 stubs 中的偏移量再减去中断入口在向量表中的偏移量,即 200+<br />

vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC) +<br />

vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的 vector_irq,减去 irq_PC<br />

是由汇 编器完成的,而后面的 vectors_start+200-stubs_start 就应该是 stubs_offset,实际上在<br />

entry-armv.S 中也是这样定义的<br />

进入中断后在 irq 模式完成什么工作?<br />

vector_irq 是通过宏来 vector_stub 定义的:<br />

.macro vector_stub, name, mode, correction=0<br />

.align 5<br />

vector_\name:<br />

.if \correction<br />

sub lr, lr, #\correction<br />

.endif<br />

@<br />

@ Save r0, lr_ (parent PC) and spsr_<br />

@ (parent CPSR)<br />

@<br />

stmia sp, {r0, lr} @ save r0, lr<br />

mrs lr, spsr<br />

str lr, [sp, #8] @ save spsr<br />

@<br />

@ Prepare for SVC32 mode. IRQs remain disabled.<br />

@


mrs r0, cpsr<br />

eor r0, r0, #(\mode ^ SVC_MODE)<br />

msr spsr_cxsf, r0<br />

@<br />

@ the branch table must immediately follow this code<br />

@<br />

and lr, lr, #0x0f<br />

mov r0, sp<br />

ldr lr, [pc, lr, lsl #2]<br />

movs pc, lr @ branch to handler in SVC mode<br />

.endm<br />

vector_stub irq, IRQ_MODE, 4<br />

.long __irq_usr @ 0 (USR_26 / USR_32)<br />

.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)<br />

.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)<br />

.long __irq_svc @ 3 (SVC_26 / SVC_32)<br />

.long __irq_invalid @ 4<br />

......<br />

从 上面这段代码可以看出,vector_irq 把发生中断时的 r0,PC-4 以及 CPSR 压栈(注意,压入的的 irq 模<br />

式的堆栈),把中断栈的栈指针赋给 r0,最后根据原来发生中断时 CPU 所处的工作模式(CPSR 的低 4 位)<br />

找到相应的入口函数,在进入 svc 模式后进一步处理中断。<br />

__irq_usr 完成的工作是什么?<br />

它主要通过调用宏 usr_entry 进一步保存现场,然后调用 irq_handler 进行中断处理,保存的栈结构如下:<br />

-1<br />

CPSR<br />

PC-4<br />

LR<br />

SP<br />

R12<br />

...<br />

R2<br />

R1<br />

R0<br />

其中的 LR,SP 是 USR 模式下的,R0,CPSR,PC-4 是从中断栈中拷贝的


irq_handler 的作用是什么?<br />

• 它首先通过宏 get_irqnr_and_base 获得中断号,存在 r0,然后把上面建立的 pt_regs 结构的指针,<br />

也就是 sp 值赋给 r1,把调用宏 get_irqnr_and_base 的位置作为返回地址(为了处理下一个中<br />

断???)然后调用 asm_do_IRQ 进一步处理中断,以上这些操作都在建立在获得中断号的前提下,<br />

也就是有中断发生<br />

• 代码实现<br />

.macro irq_handler<br />

1: get_irqnr_and_base r0, r6, r5, lr<br />

movne r1, sp<br />

@<br />

@ routine called with r0 = irq number, r1 = struct pt_regs *<br />

@<br />

adrne lr, 1b<br />

bne asm_do_IRQ<br />

.endm<br />

具体的中断处理会在 asm_do_IRQ 根据中断号调用相应的中断处理程序完成。<br />

----<br />

1.3 s3c2410 里面的中断控制器的细节分析:<br />

从这里:<br />

可以看出: s3c2410a 可以接收 56 个中断源的请求 。<br />

这些中断源来自来自两部分:内部外设,比如 DMA 控制器,uart ,i2c


第二部分是外部中断请求的 pin 脚。<br />

流程是这样:<br />

当中断控制器接收到中断请求后, 经过仲裁过程后(选择了一个优先级高的 IRQ 或者 FIQ),然后才向 CPU<br />

发出 IRQ 或者 FIQ 的请求。<br />

至于到底这个中断源产生的 IRQ 还是 FIQ , 这个要根据 interrupt mode register 设置了:<br />

从这里我们也可以看出, IRQ 和 FIQ 只是两种模式而已。 也就是说 , 当一个中断源被配置成 IRQ 或者<br />

FIQ 的时候 , 中断控制器处理她的 request 的方式是不同的。<br />

那是不是每个中断源可以随便设置自己的模式呢?显然不行。 (想想, 如果发给大家的都是 VIP 卡, 那<br />

这个 VIP 卡显然就是普通的卡, 被少数人拥有的才是 VIP 卡)<br />

显然,只有紧急的中断才可以配置成 FIQ , 也就是只有一个中断源才可以配置成 FIQ 模式,也就是 VIP<br />

只有一个。<br />

FIQ 可以理解成火车站里面的快速通道,不需要检票 ,直接上车。


翻译出来是这样:<br />

过程很简单, 2410a 两个中断未决寄存器 SRCPND 和 INTPND 。<br />

过程这样:当中断源请求服务后, SRCPND 对应 set 1 , 经过仲裁后, 对应的 INTPND 的 bit 会被被置 1<br />

(前提是没有 mask 这个中断源) , 如果 mask 了, INTPND 的对应的 bit 就不会自动 set 1 。<br />

接下来,就取决于 CPSR 的 F-bit 和 I-bit 了, 如果他们置 1 了,INTPND 也置 1 , 那么就调用中断服<br />

务 routine , 否则如果 F-bit 或者 I-bit 是 0 , 那么就不会调用中断服务 routine 了。<br />

所以 ,从这里就可以看出 SRCPND 和 INTPND 的 关系了: SRCPND set 1 表示中断源产生了 request ,<br />

而INTPND 仅仅表示这个中断源没有被mask掉 ,中断控制器可以向CPU发出request了,以上这些都是从<br />

interrupt controller的角度来说的, 至于CPU 处不处理(也就是是否调用中断处理routine)就得 看 CPSR<br />

寄存器里面F-bit和I-bit了, 如果都被set成0了, 那么显然CPU是不会鸟 中断控制器的(从英文来看:<br />

(If the F-bit of PSR in ARM920T CPU is set to 1, the CPU does not accept the Fast Interrupt Request (FIQ) from the<br />

interrupt controller. Likewise, If I-bit of the PSR is set to 1, the CPU does not accept the Interrupt Request (IRQ) from the<br />

interrupt controller. So, the interrupt controller can receive interrupts by clearing F-bit or I-bit of the PSR to 0 and setting the<br />

corresponding bit of INTMSK to 0 , 这里用了 accept ,那就是说 interrupt发了request ,可是CPU 不能accept) 。


可以看出, 2410a的中断控制器 ,总共有5个寄存器 :SRCPND , INTMR , MR ,PRI , INTPND<br />

可以看出 , FIQ是不需要仲裁的, 显然从这点可以看出 FIQ 的效率肯定比IRQ高 ,因为FIQ必须要仲裁。<br />

来自中断源的中断请求必须首先在SRCPND 寄存器里面注册一下(即对应的bit 置1) 。<br />

这些请求又分为两组, 至于怎么分,就得看 中断模式寄存器了。<br />

关于SRCPND 这里面说的很明确了, 就是 只要触发了中断, SRCPND对应的bit就会被自动置1 , 仅仅表示产生了中断, 等<br />

待着被服务(<strong>kernel</strong>调用interrupt service) 。 而无论INTMASK对应的bit 是否是1 , SRCPND都会被自动置1 。<br />

这一点 ,上面已经说的很清楚了。<br />

这个说的就是 关于如何清除 SRCPND , 以及何时清除的问题了, 还有就是如果 你不clear SRCPND相应的bit会导致什么后<br />

果:<br />

显然上面说的很清楚了, SRCPND 仅仅表示 中断请求的状态, 如果 <strong>kernel</strong>都处理了这个中断(调用了中断处理函数) ,就<br />

应该告诉 中断控制器(我已经处理完了, 你tmd别烦了) 。<br />

如果没有清除会有什么样的后果: 我想, 中断控制器可不知道<strong>kernel</strong>已经处理完了这个中断, 还在那里等着<strong>kernel</strong>去处理呢,<br />

这样<strong>kernel</strong>也傻了, 就会去反复的处理的这个中断)。


打个比方: 一个傻子让妈妈喂饭: “妈妈, 我饿了”(SRCPND 对应bit 置1 ) , 但是啥子不知道吃多少<br />

才能饱(脑子坏掉了,要不怎么傻呢?) 。 妈妈知道喂两碗就行了 。 所以当妈妈喂了两碗之后, 不再喂了(相当于中断处<br />

理完毕) 。<br />

然后告诉傻子, 行了 ,饱了(相当于SRCPND 清0) ,傻子相信妈妈的话, only相信妈妈的话 。<br />

否则 ,如果妈妈不说“行了, 吃饱了,不要再吃了”这样的话, 傻子就一个劲的问问“ 妈妈, 我饿,俄” ,结果妈妈(也<br />

是个傻妈妈,难怪啥子是遗传的) ,一个劲的喂 , 结果什么活也干不了了,同时也把傻儿子撑死了。<br />

我怎么会突然想到这么一个比喻, 我真是个天才啊! --bob<br />

?对于CPU都是这样的吗? 就是 interrupt service<br />

routine 里面必须清除中断的标志bit吗?<br />

我的理解,都应该这样的。<br />

这里面说得也很清楚了, 就是 INTPND 中的每一个bit 表示是否有对应的中断请求,这个请求没有被屏蔽掉 ,正等着CPU来<br />

处理这个中断(即to be serviced) 。 一旦这个某个bit被置1了, 就表示这个中断的优先级是最高(因为是经过仲裁的) 。<br />

这样的话, 用户就可以读INTPND寄存器, 哪个bit置1了(32个bit只能有一个bit是1) , 就表示哪个中断源产生了中断。<br />

其实关于INTPND , 中断控制器的介绍的开头说的比较清楚了:<br />

也就是说, 经过仲裁后,最后的结果(也就是中断号) 被写入pending 寄存器,表明在许多个中断源中,到底谁产生了中断。<br />

---总结: 一句话 INTERRUPT PENDING REGISTER 就表示最终谁触发了中断。


下面要重点注意的问题就是:在中断处理routine里面一定要清除INTPND , 从上面的意思来看, 最便利的办法就是 寄存器原<br />

来是什么value ,就再写进去(读出来,再写进去)<br />

其实:这个就可以衍生出一个比较通用的考题:(搞driver开发的人一定要能答得上,否则东家不会录用你的)<br />

总结: 以IRQ为例,FIQ于此相同。<br />

画个图:<br />

CPU 中断控<br />

制器<br />

CPSR 的 F-bit 和 I-bit<br />

决定了当中断控制器<br />

送上来 request 后,<br />

CPU 是否处理<br />

S3c2410 中断原理图 , drawed by<br />

bob_zhang2004@163.com<br />

INTMASK ,决定中断<br />

request 能不能送上来<br />

INT SRC1<br />

INT SRC2


1> 就是 当硬件出发了中断,<br />

2> SRCPND对应的bit自动置1 ,<br />

3> INTMASK 要分两种情况:<br />

如果INTMASK对应的bit是1 , 那么INTPND对应的bit就不会自动置1 ,表示 中断控制器肯定不会request IRQ to CPU 。<br />

如果INTMASK对应的bit是0 , 那么INTPND对应的bit就 会自动置1 , 那么中断控制器 会 request IRQ to CPU 。<br />

4> 如果INTPND对应的bit就 被置 1 了, 接下来还要分两种情况:<br />

如果I-bit 是0 , 表示CPU 可以 accept 中断控制器的 IRQ request , 那么CPU 就会执行 中断处理routine 。<br />

如果I-bit 是1 , 表示 CPU 不能 accept 中断控制器的 IRQ request , 就不会调用 中断处理routine<br />

又想到一个比喻: 古代奸臣误国,好多正直大臣的奏折并不能被直接传递到皇帝手中 ,比如大奸臣魏忠贤。<br />

魏忠贤好比是中断控制器 , 皇帝好比是CPU 。<br />

奏章要首先经过魏忠贤的手, 如果魏忠贤屏蔽掉某个大臣的奏章(mask),那么皇帝肯定没有机会看了。<br />

但是假使奏章通过魏忠贤(interrupt controller)传给皇帝了, 可是皇帝是个糊涂蛋, 只知玩乐,误了朝政, 奏折堆积如山(就<br />

相当于 F-bit=1,I-bit=1)了。<br />

所以封建国家, 既要有好大臣(狄仁杰,什么奏章都敢上奏,不管是否是弹劾自己的), 又要有好皇帝(唐太宗,勤于政事)<br />

啊。<br />

当一个中断控制器连接了128个中断源的时候, CPU怎么知道哪个中断源产生<br />

了中断呢?继而调用对应的中断处理函数呢?<br />

一般的原理都是这样的:<br />

显然,要回答这个问题, 必须要搞清楚, CPU是肯定不知道哪个中断源产生了中断的, 谁知道呢?只有中断控制器才知<br />

道的, 那么,CPU就要查询中断控制器就好了。 问题是中断控制器的那个register 的标志bit 被置1 , 肯定是硬件做的,<br />

知道这些就足够了。<br />

当中断源触发了一个中断的时候, 中断控制器要提供一个寄存器来标志哪个源产生中断, 类似2410 就提供INTPND(32<br />

个bit,bit置1 就表示对应的source产生了中断) , 那么CPU就会读取这个寄存器,以确定中断号(get_irqnr_and_base<br />

这是个汇编宏)。 问题在于CPU怎么知道要去读呢? 显然要中断控制器给CPU发一个IRQ的request , 这样CPU才会去读<br />

(实际上是一段汇编代码) 。<br />

继而才会调用asm_do_IRQ(irq_number)来处理中断, 继而可以通过irq号找到对应中断的中断描述符(irq descriptor),找<br />

到了中断描述符,就找到了,handler , 然后 调用 ->handler() 就可以处理中断了。<br />

----------<br />

1.4 外部中断控制器的分析(与GPIO有关)<br />

分析完了中断控制器的部分。<br />

我们还要接下来看看 datasheet的什么部分与中断有关。<br />

就是 P276 页 里面的关于外部中断的部分 ,还有 GPF和GPG的部分


下面涉及的就是 外部的设备要想产生中断, 要怎么样连接的问题,s3c2410 里面显然是采取GPIO复用端口的方式。<br />

一个外部设备, 如果可以产生中断, 就可以接在GPF或者GPG的端口上,然后配置端口的模式为 INT mode 。<br />

如图:<br />

--<br />

还有 GPG 也是类似。<br />

接下来这个描述就是中断触发的方式有关了:<br />

我们知道, 中断出发有好几种:低电平触发,高电平触发, 上升沿触发,下降沿触发,both edge触发。<br />

S3c2410a 提供了 24个外部中断, 那么就提供了3个寄存器来配置这24个 GPIO口的中断触发方式。<br />

下面是关于 GPIO 这端的 外部中断未决和外部中断屏蔽寄存器:


显然是 这是最底层的中断控制了, 因为 设备是通过 GPIO pin 脚连上来的, 这是第一步。<br />

上面的图也许应该改一改了<br />

CPU<br />

CPSR 的 F-bit 和 I-bit<br />

决定了当中断控制器<br />

送上来 request 后,<br />

CPU 是否处理<br />

GPIO 的 EINTMASK 和<br />

EINTPND register 来控<br />

制,信号能否发进去<br />

中断控<br />

制器<br />

INTPND<br />

[31:0]<br />

INTMASK ,决定中断<br />

request 能不能送上来<br />

INT SRC1<br />

INT SRC2<br />

EXINT<br />

Drawed by bob_zhang2004@163.com

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!