有一个问题:
在datasheet中清楚的说明s3c6410一共有64个中断,
但是dm9000的驱动中request_irq()的中断号却是108.
如下图所示: cat /proc/interrupts

为什么申请出来的中断号是108呢? ?
从中断引脚的定义可以看出:
EINT(7) = 7+EINT_BASE
EINT_BASE=64+5+32=101
7+64+5+32=108,里面各个数值都是什么意思呢?
要弄明白这个需要详细的了解一下arm中断的各个流程.
1. 系统中断的初始化.
2. 中断产生并进入中断处理函数
一. 中断初始化
从start_kernel开始看起,下面有四个函数跟中断初始化有关系
1. 中断向量与中断函数的拷贝
在arch/arm/kernel/traps.c中
2.初始化irq_desc结构体数组
在kernel/irq/irqdesc.c中,其中NR_IRQS=246
start_kernel
--> early_irq_init
#define ARCH_IRQ_INIT_FLAGS (IRQ_NOREQUEST | IRQ_NOPROBE)
start_kernel
--> early_irq_init
--> desc_set_defaults
3. s3c6410内部64 个中断初始化
arch/arm/mach-s3c64xx/irq.c:s3c64xx_init_irq[55]: vic0_valid=0xffffff7f, vic1_valid=0xffffffff
arch/arm/mach-s3c64xx/irq.c:s3c64xx_init_irq[56]: VA_VIC0=0xf6000000, IRQ_VIC0_BASE=0x20
arch/arm/mach-s3c64xx/irq.c:s3c64xx_init_irq[57]: VA_VIC1=0xf6010000, IRQ_VIC1_BASE=0x40
#define VA_VIC0 (S3C_VA_IRQ + 0x00)
#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */
#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
#define S3C_ADDR_BASE 0xF6000000
3.1 禁止所有的中断
3.2 对64个内部中断的irq_desc重新设置其标志位
vic0的中断号是32-64;
vic1的中断号是65-96;
3.2.1初始化irq_desc中的标志位state_use_accessors为IRQF_VALID|IRQF_PROBE
4. 外部中断的初始化
start_kernel
--> do_initcalls()
--> s3c64xx_init_irq_eint()
在include/linux/irq.h中
start_kernel
--> do_initcalls()
--> s3c64xx_init_irq_eint()
--> irq_set_chained_handler
irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
start_kernel
--> do_initcalls()
--> s3c64xx_init_irq_eint()
--> irq_set_handler
在kernel/irq/chip.c中
二.以usr_irq为例分析中断调用过程
1. 中断向量表
中断向量表在arch/arm/kernel/entry-armv.S中
a.
当有irq中断发生时,就会调用vector_irq.但是vector_irq在哪呢?
搜一下linux源代码也没有找到一个vector_irq,难道是弄错了?
实际上vector_irq是通过vector_stub这个宏来定义的.
b. 跳转的地址是怎么确定的呢?
以 b vector_irq + stubs_offset 为例
在entry-armv.S中有:
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
所以 vector_irq + stubs_offset =
vector_irq + __vectors_start + 0x200 - __stubs_start =
(vecor_irq - __stubs_start) + (__vectors_start + 0x200)
即: 相对于中断函数首地址的偏移 + 中断函数首地址
在arch/arm/kernel/entry-armv.S中有
2. 用vector_stub 产生中断函数
把vector_stub irq, IRQ_MODE, 4展开后是:
在arch/arm/kernel/entry-armv.S中
以spsr中的低四位为索引,对pc值进行调整:
如果上述是从user模式进入,则会跳到__irq_users处执行
如果上述是从svc模式进入,则会跳到 __irq_svc处执行
3. 进入irq_usr
从中断向量表中查找到中断函数,然后进入中断函数的处理过程__irq_usr
在arhc/arm/kernel/entry-armv.S中
__irq_usr
--> irq_handler
__irq_usr
--> irq_handler
--> arch_irq_handler_default
3.1 获取中断号
从寄存器中读取的中断号的范围是(0-64),但是实际上这儿把中断号读取出来之后,都加了一个固定值32
所以真正的中断号的取值范围是(0-64)+32.
在arch/arm/include/asm/entry-macro-vic2.S中
a. teq \irqstat, #0 判断irqstat是不是等于0,如果等于0,则cpsr中Z置位.
后面的指令都代着eq,如果Z=1则执行,反之则不执行
b. clz {cond} Rd, Rm
对Rm中leading zeros的个数进行计数,将结果存在Rd中P { margin-bottom: 0.08in; direction: ltr; color: rgb(0, 0, 0); text-align: justify; }P.western { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }P.cjk { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }P.ctl { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }
(leading zeros: Rm中从高位向低位进行查找,直至遇到1为止,将0的个数统计出来)
例: 0x0000 0000 --> 32;
0x0000 000F --> 24;
0x0F00 0000 --> 4 ;
0x8000 0000 --> 0;
c. 总结一下上面的代码:
如果vic0产生了中断, 读取中断号的值,判断中断号是不是0,不是0,则跳到clz句判断中断号;
3.2 进入c语言的中断处理函数asm_do_IRQ
在arch/arm/kernel/irq.c中
__irq_usr
--> irq_handler
--> arch_irq_handler_default
--> asm_do_IRQ
__irq_usr
--> irq_handler
--> arch_irq_handler_default
--> asm_do_IRQ
--> generic_handler_irq
--> asm_do_IRQ
--> generic_handler_irq
--> generic_handler_irq_desc
注意,上面的desc->handle_irq是在
s3c64xx_init_irq_eint()
中注册的,
以dm9000的eint(7)为例说明一下:
irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
即注册 desc->handle_irq= s3c_irq_demux_eint4_11
3.3 分发外部中断
--> asm_do_IRQ
--> generic_handler_irq
--> generic_handler_irq_desc
--> s3c_irq_demux_eint4_11
在arch/arm/mach-s3c64xx/irq-eint.c中
--> asm_do_IRQ
--> generic_handler_irq
--> generic_handler_irq_desc
--> s3c_irq_demux_eint4_11
--> s3c_irq_demux_eint
在arch/arm/mach-s3c64xx/irq-eint.c中
从IRQ_EINT(4)开始stauts向右移1位,当status==1时,正好是IRQ_EINT(7)
IRQ_EINT(7)正好是在dm9000注册的中断号
3.4 第二次调用generic_handle_irq
这次的desc->handle_irq是谁呢?
注意,上面的desc->handle_irq也是在 s3c64xx_init_irq_eint() 中注册的,
但这次不一样的,要不成死循环了,这次是 desc->handle_irq= handle_level_irq
三.驱动调用request_irq申请中断
dm9000申请中断:
request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev);
其中: dev->irq=EINT(7)=108, irqflags=0x84=IRQF_SAMPLE_RANDOM|IRQF_SHARED
在include/linux/interrupt.h中
在kernel/irq/maage.c中
附录:
1. s3c6410的外部中断
<s3c6410英文手册_v1.2> section 12.3
s3c6410的外部中断eint0-4,共5个
外部中断0-3会触发0号中断
外部中断4-11会触发1号中断
外部中断12-19会触发32号中断
外部中断20-27会触发33号中断
2. 总结一下s3c6410的中断
在datasheet中清楚的说明s3c6410一共有64个中断,
但是dm9000的驱动中request_irq()的中断号却是108.
如下图所示: cat /proc/interrupts

为什么申请出来的中断号是108呢? ?
从中断引脚的定义可以看出:
- #define IRQ_EINT(x) S3C_EINT(x)
- #define S3C_EINT(x)
((x)
+ S3C_IRQ_EINT_BASE)
- #define S3C_IRQ_EINT_BASE S3C_IRQ(64+5)
- #define S3C_IRQ(x)
((x)
+ S3C_IRQ_OFFSET)
- #define S3C_IRQ_OFFSET (32)
EINT_BASE=64+5+32=101
7+64+5+32=108,里面各个数值都是什么意思呢?
要弄明白这个需要详细的了解一下arm中断的各个流程.
1. 系统中断的初始化.
2. 中断产生并进入中断处理函数
一. 中断初始化
从start_kernel开始看起,下面有四个函数跟中断初始化有关系
- asmlinkage void __init start_kernel(void)
- {
- setup_arch(); --> early_trap_init();
//1.中断向量表与中断函数的copy
- trap_init();
//空函数,不管它
- early_irq_init();
//2.初始化irq_desc数组
- init_IRQ();
//3.s3c6410内部64个中断的初始化
- rest_init(); //4.
do_initcalls() --> s3c64xx_init_irq_eint();外部中断的初始化
- }
1. 中断向量与中断函数的拷贝
在arch/arm/kernel/traps.c中
- void __init early_trap_init(void)
- {
- //这儿定义了CONFIG_CPU_USE_DOMAINS
- unsigned long vectors = CONFIG_VECTORS_BASE;
//BASE=0xffff0000
- extern char __stubs_start[], __stubs_end[];
- extern char __vectors_start[], __vectors_end[];
- extern char __kuser_helper_start[], __kuser_helper_end[];
- int kuser_sz = __kuser_helper_end - __kuser_helper_start;
- //copy中断向量表到0xffff000
- memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
- //copy中量函数到0xffff000+0x200
- memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
-
//copy中断向量表到0xffff000
- memcpy((void
*)vectors
+ 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
- kuser_get_tls_init(vectors);
- memcpy((void
*)(vectors
+ KERN_SIGRETURN_CODE
- CONFIG_VECTORS_BASE),
- sigreturn_codes, sizeof(sigreturn_codes));
- memcpy((void
*)(vectors
+ KERN_RESTART_CODE
- CONFIG_VECTORS_BASE),
- syscall_restart_code, sizeof(syscall_restart_code));
- flush_icache_range(vectors, vectors
+ PAGE_SIZE);
- modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
- }
2.初始化irq_desc结构体数组
在kernel/irq/irqdesc.c中,其中NR_IRQS=246
start_kernel
--> early_irq_init
- struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp
= {
- [0 ... NR_IRQS-1]
= {
- .handle_irq = handle_bad_irq,
- .depth = 1,
- .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
- }
- };
- int __init early_irq_init(void)
- {
- int count, i, node
= first_online_node;
- struct irq_desc *desc;
- init_irq_default_affinity();
- desc = irq_desc;
- count = ARRAY_SIZE(irq_desc);
- for (i
= 0; i
< count; i++)
{
- desc[i].kstat_irqs
= alloc_percpu(unsigned
int);
- alloc_masks(&desc[i], GFP_KERNEL, node);
- raw_spin_lock_init(&desc[i].lock);
- lockdep_set_class(&desc[i].lock,
&irq_desc_lock_class);
- desc_set_defaults(i,
&desc[i], node);
- }
- return arch_early_irq_init();
- }
start_kernel
--> early_irq_init
--> desc_set_defaults
- static void desc_set_defaults(unsigned
int irq, struct irq_desc
*desc,
int node)
- {
- int cpu;
- desc->irq_data.irq
= irq;
- desc->irq_data.chip
= &no_irq_chip;
- desc->irq_data.chip_data
= NULL;
- desc->irq_data.handler_data
= NULL;
- desc->irq_data.msi_desc
= NULL;
- irq_settings_clr_and_set(desc,
~0, _IRQ_DEFAULT_INIT_FLAGS);
//初始化为IRQ_NOREQUEST | IRQ_NOPROBE
- irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
- desc->handle_irq
= handle_bad_irq;
- desc->depth
= 1;
- desc->irq_count
= 0;
- desc->irqs_unhandled
= 0;
- desc->name
= NULL;
- for_each_possible_cpu(cpu)
- *per_cpu_ptr(desc->kstat_irqs, cpu)
= 0;
- desc_smp_init(desc, node);
- }
3. s3c6410内部64 个中断初始化
- 在arch/arm/kernel/iqr.c中
- void __init init_IRQ(void)
- {
- machine_desc->init_irq();
- }
- 在arch/arm/mach-s3c64xx/mach-smdk6410.c中
- MACHINE_START(SMDK6410,
"SMDK6410")
- .boot_params = S3C64XX_PA_SDRAM
+ 0x100,
- .init_irq = s3c6410_init_irq,
- .map_io = smdk6410_map_io,
- .init_machine = smdk6410_machine_init,
- .timer =
&s3c24xx_timer,
- MACHINE_END
- 在arch/arm/mach-s3c64xx/s3c6410.c中
- void __init s3c6410_init_irq(void)
- {
- /* VIC0
is missing IRQ7, VIC1
is fully populated.
*/
- s3c64xx_init_irq(~0
& ~(1
<< 7),
~0);
- }
- //VIC0中的第7号中断是保留的,
- 在arch/arm/mach-s3c64xx/iqr.c中
- void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
- {
- /* initialise the pair of VICs
*/
- vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0);
- vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0);
- /* add the timer
sub-irqs
*/
- s3c_init_vic_timer_irq(5, IRQ_TIMER0);
- s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs));
- }
arch/arm/mach-s3c64xx/irq.c:s3c64xx_init_irq[56]: VA_VIC0=0xf6000000, IRQ_VIC0_BASE=0x20
arch/arm/mach-s3c64xx/irq.c:s3c64xx_init_irq[57]: VA_VIC1=0xf6010000, IRQ_VIC1_BASE=0x40
#define VA_VIC0 (S3C_VA_IRQ + 0x00)
#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */
#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
#define S3C_ADDR_BASE 0xF6000000
- void __init vic_init(void __iomem
*base, unsigned
int irq_start,
- u32 vic_sources, u32 resume_sources)
- {
- //读取vid,没有找到0xfe0这个是代表什么,不知道也没有多大影响
- for (i
= 0; i
< 4; i++)
{
- u32 addr =
((u32)base
& PAGE_MASK)
+ 0xfe0 +
(i * 4);
- cellid |=
(readl(addr)
& 0xff)
<< (8
* i);
- }
- //VIC @f6000000: id 0x00041192, vendor 0x41
- vendor = (cellid
>> 12)
& 0xff;
- switch(vendor)
{
- case AMBA_VENDOR_ST:
- vic_init_st(base, irq_start, vic_sources);
- return;
- default:
- printk(KERN_WARNING
"VIC: unknown vendor, continuing anyways\n");
- case AMBA_VENDOR_ARM:
- break;
- }
- vic_disable(base);
//3.1 禁止所有的中断
-
- vic_clear_interrupts(base);
//PL190???这TMD是什么玩意?
- vic_init2(base);
//又有PL190?我准备放弃搞懂这个函数
- vic_set_irq_sources(base, irq_start, vic_sources);
//3.2 设置irq_desc中标志为可用
- vic_pm_register(base, irq_start, resume_sources); //3.3
- }
3.1 禁止所有的中断
- static void __init vic_disable(void __iomem
*base)
- {
- //32位的寄存器,每一位代表一个中断
- writel(0, base
+ VIC_INT_SELECT);
//将所有的中断都设为IRQ,不是FIQ
- writel(0, base
+ VIC_INT_ENABLE);
//所有的中断都为disable状态
- writel(~0, base
+ VIC_INT_ENABLE_CLEAR);
//写1是要清除VIC_INT_ENABLE的中断使能
- writel(0, base
+ VIC_IRQ_STATUS); //清除状态寄存器
- writel(0, base
+ VIC_ITCR);
//TMD,VIC test control reg??
- writel(~0, base
+ VIC_INT_SOFT_CLEAR); //写1是要清除VICSOFTINT寄存器
- }
- static void __init vic_set_irq_sources(void __iomem
*base, unsigned
int irq_start, u32 vic_sources)
- {
- //vic_source是32bit,其中每1位置1的代表启用一个中断源
- //调用时vic0=~0
&
~(1
<< 7),即不添加中断源7
- for (i
= 0; i
< 32; i++)
{
- if (vic_sources
& (1
<< i))
{ //只对置1的中断源进行处理
- unsigned int irq
= irq_start + i;
//vic的中断号是从iqr_start=32或64开始
- //通过中断号找到irq_desc结构体,并设置irq_desc的irq_data.chip与handle_irq
- irq_set_chip_and_handler(irq,
&vic_chip, handle_level_irq);
- irq_set_chip_data(irq, base);
- set_irq_flags(irq, IRQF_VALID
| IRQF_PROBE);
//1.2.1重新设置irq_desc中的标志位为IRQF_VALID
- }
- }
- }
vic1的中断号是65-96;
3.2.1初始化irq_desc中的标志位state_use_accessors为IRQF_VALID|IRQF_PROBE
- void set_irq_flags(unsigned
int irq, unsigned
int iflags)
- {
- unsigned long clr = 0,
set = IRQ_NOREQUEST
| IRQ_NOPROBE | IRQ_NOAUTOEN;
- if (irq
>= nr_irqs)
{
- printk(KERN_ERR
"Trying to set irq flags for IRQ%d\n", irq);
- return;
- }
- //清除在early_irq_init中设置的IRQ_NOREQUEST|IRQ_NOPROBE标志,重新设为
- //IRQ_VALID|IRQ_PROBE
- if (iflags
& IRQF_VALID)
- clr |= IRQ_NOREQUEST;
- if (iflags
& IRQF_PROBE)
- clr |= IRQ_NOPROBE;
- if (!(iflags
& IRQF_NOAUTOEN))
- clr |= IRQ_NOAUTOEN;
-
- irq_modify_status(irq, clr,
set &
~clr);
- }
start_kernel
--> do_initcalls()
--> s3c64xx_init_irq_eint()
- static int __init s3c64xx_init_irq_eint(void)
- {
- int irq;
- //6410的外部中断号是从IRQ_EINT(0)=101 到 IRQ_EINT(27)=128
- //对101-128的外部中断,初始化其irq_desc结构体,
- //其中断处理函数是handle_level_irq
- for (irq
= IRQ_EINT(0); irq
<= IRQ_EINT(27); irq++)
{
- irq_set_chip_and_handler(irq,
&s3c_irq_eint, handle_level_irq);
- irq_set_chip_data(irq,
(void *)eint_irq_to_bit(irq));
- set_irq_flags(irq, IRQF_VALID);
- }
- //对未映射之前的中断号为(0,1,32,33)-->现在的(32,33,64,65)
- //对这四个中断注册其中断处理函数
- irq_set_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3);
- irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
- irq_set_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19);
- irq_set_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27);
- return 0;
- }
start_kernel
--> do_initcalls()
--> s3c64xx_init_irq_eint()
--> irq_set_chained_handler
irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
- static inline void
- irq_set_chained_handler(unsigned
int irq, irq_flow_handler_t handle)
- {
- __irq_set_handler(irq, handle, 1,
NULL);
//irq=33(33-32=1即INT_EINT1)
- }
--> do_initcalls()
--> s3c64xx_init_irq_eint()
--> irq_set_handler
在kernel/irq/chip.c中
- void __irq_set_handler(unsigned
int irq, irq_flow_handler_t handle,
int is_chained,
const char *name)
- {
- //其它的省略只看办正事的地方
- desc->handle_irq
= handle; //注册中断处理函数
- desc->name
= name;
//name=NULL
- }
二.以usr_irq为例分析中断调用过程
1. 中断向量表
中断向量表在arch/arm/kernel/entry-armv.S中
- .globl __vectors_start
- __vectors_start:
- ARM( swi SYS_ERROR0 )
- THUMB( svc #0 )
- THUMB( nop )
- W(b) vector_und
+ stubs_offset
- W(ldr) pc,
.LCvswi + stubs_offset
- W(b) vector_pabt
+ stubs_offset
- W(b) vector_dabt
+ stubs_offset
- W(b) vector_addrexcptn
+ stubs_offset
- W(b) vector_irq
+ stubs_offset
- W(b) vector_fiq
+ stubs_offset
- .globl __vectors_end
- __vectors_end:
搜一下linux源代码也没有找到一个vector_irq,难道是弄错了?
实际上vector_irq是通过vector_stub这个宏来定义的.
b. 跳转的地址是怎么确定的呢?
以 b vector_irq + stubs_offset 为例
在entry-armv.S中有:
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
所以 vector_irq + stubs_offset =
vector_irq + __vectors_start + 0x200 - __stubs_start =
(vecor_irq - __stubs_start) + (__vectors_start + 0x200)
即: 相对于中断函数首地址的偏移 + 中断函数首地址
在arch/arm/kernel/entry-armv.S中有
- __stubs_start:
- vector_stub irq, IRQ_MODE, 4
;马上分析这个宏的作用
- .long __irq_usr @ 0
(USR_26 / USR_32)
- .long __irq_invalid @ 1
(FIQ_26 / FIQ_32)
- .long __irq_invalid @ 2
(IRQ_26 / IRQ_32)
- .long __irq_svc @ 3
(SVC_26 / SVC_32)
- .long __irq_invalid @ 4
- .long __irq_invalid @ 5
把vector_stub irq, IRQ_MODE, 4展开后是:
在arch/arm/kernel/entry-armv.S中
- vector_stub irq, IRQ_MODE, 4
- .macro vector_stub,
name, mode, correction=0
- vector_irq: ;看到没有vector_irq,它老人家终于出来了
- .if 4
- sub lr, lr, #4
- .endif
-
- ;将r0,lr,spsr压栈
- stmia sp, {r0, lr} ;将r0,lr寄存器压栈
- mrs lr, spsr
;将spsr保存在lr中
- str lr,
[sp, #8]
;将lr压栈,即保存spsr
-
- ;将SVC_MODE保存到spsr中,准备切换到svc
- mrs r0, cpsr ;读取cpsr到r0
- eor r0, r0, #(IRQ_MODE ^ SVC_MODE
| PSR_ISETSTATE)
;将r0设为SVC_MODE
- msr spsr_cxsf, r0 ;将r0中的SVC_MODE写到spsr中,通过movs就可以切换到SVC模式了
- and lr, lr, #0x0f
;usr模式(10000)的低4位==0,SVC模式(10011)的低4位==3,
- THUMB( adr r0, 1f )
;这几句没有完全弄明白
- THUMB( ldr lr,
[r0, lr,
lsl #2] )
;
- mov r0, sp ;
- ARM( ldr lr,
[pc, lr,
lsl #2] )
;感觉是要跳到这个宏的下几句进行
- movs pc, lr
;跳转到下一条指令,并cpsr会被spsr覆盖即切换到SVC模式
- ENDPROC(vector_irq)
- .align 2
- 1:
- .endm
如果上述是从user模式进入,则会跳到__irq_users处执行
如果上述是从svc模式进入,则会跳到 __irq_svc处执行
3. 进入irq_usr
从中断向量表中查找到中断函数,然后进入中断函数的处理过程__irq_usr
在arhc/arm/kernel/entry-armv.S中
- .align 5
- __irq_usr:
- usr_entry
- kuser_cmpxchg_check
- get_thread_info tsk
- irq_handler ;irq_handler继续调用
- mov why, #0
- b ret_to_user_from_irq
- UNWIND(.fnend )
- ENDPROC(__irq_usr)
--> irq_handler
- 在arch/arm/kernel/entry-armv.S中
- .macro irq_handler
- arch_irq_handler_default
- 9997:
- .endm
--> irq_handler
--> arch_irq_handler_default
- 在arm/include/asm/entry-macro-multi.S中
- .macro arch_irq_handler_default
- get_irqnr_preamble r5, lr
- 1: get_irqnr_and_base r0, r6, r5, lr ;3.1获取中断号
- movne r1, sp
- adrne lr, BSYM(1b)
- bne asm_do_IRQ ;3.2进入中断处理函数
- 9997:
- .endm
3.1 获取中断号
从寄存器中读取的中断号的范围是(0-64),但是实际上这儿把中断号读取出来之后,都加了一个固定值32
所以真正的中断号的取值范围是(0-64)+32.
在arch/arm/include/asm/entry-macro-vic2.S中
.macro get_irqnr_preamble, base, tmp
注意:- ldr \base,
=VA_VIC0
- .endm
- @base是VA_VIC0
- .macro get_irqnr_and_base, irqnr, irqstat, base,
tmp
- mov \irqnr, #IRQ_VIC0_BASE
+ 31 //为什么这儿要加上31呢?是因为clz这个指令
- ldr \irqstat,
[ \base, # VIC_IRQ_STATUS
] //读取VIC_IRQ_STATUS的值,也就是中断号的值(加上"的值"比较确切)
- teq \irqstat, #0
//看irqstat是不是0,如果是0,标志位Z=1
- @ otherwise try vic1 //下面的一串eq执行说明vic0没有产生中断irqstat==0
- addeq \tmp, \base, #(VA_VIC1
- VA_VIC0)
//
- addeq \irqnr, \irqnr, #(IRQ_VIC1_BASE
- IRQ_VIC0_BASE)
- ldreq \irqstat,
[ \tmp, # VIC_IRQ_STATUS
]
- teqeq \irqstat, #0
//看irqstat是不是0,如果是0,标志位Z=1
- //下面两句ne执行说明Z=0, 即irqstat不为0,再即有中断产生
- clzne \irqstat, \irqstat
//判断是哪一位产生了中断,但是clz是从高向低计数的,实际的中断号是(32-highnr)
- subne \irqnr, \irqnr, \irqstat
//irqnr = (IRQ_BASE+31)-highnr =
IRQ_BASE+(31-high_nr)也就是加上实际的中断号
- .endm
a. teq \irqstat, #0 判断irqstat是不是等于0,如果等于0,则cpsr中Z置位.
后面的指令都代着eq,如果Z=1则执行,反之则不执行
b. clz {cond} Rd, Rm
对Rm中leading zeros的个数进行计数,将结果存在Rd中P { margin-bottom: 0.08in; direction: ltr; color: rgb(0, 0, 0); text-align: justify; }P.western { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }P.cjk { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }P.ctl { font-family: "細明體","MingLiU",monospace; font-size: 12pt; }
(leading zeros: Rm中从高位向低位进行查找,直至遇到1为止,将0的个数统计出来)
例: 0x0000 0000 --> 32;
0x0000 000F --> 24;
0x0F00 0000 --> 4 ;
0x8000 0000 --> 0;
c. 总结一下上面的代码:
如果vic0产生了中断, 读取中断号的值,判断中断号是不是0,不是0,则跳到clz句判断中断号;
3.2 进入c语言的中断处理函数asm_do_IRQ
在arch/arm/kernel/irq.c中
__irq_usr
--> irq_handler
--> arch_irq_handler_default
--> asm_do_IRQ
- asm_do_IRQ(unsigned
int irq, struct pt_regs
*regs)
- {
- struct pt_regs *old_regs
= set_irq_regs(regs);
- irq_enter();
- if (unlikely(irq
>= nr_irqs))
{ //判断中断号是否超出范围
- if (printk_ratelimit())
- printk(KERN_WARNING
"Bad IRQ%u\n", irq);
- ack_bad_irq(irq);
- } else
{
- generic_handle_irq(irq);
//封装,继续调用
- }
- /* AT91 specific workaround
*/
- irq_finish(irq);
- irq_exit();
- set_irq_regs(old_regs);
- }
__irq_usr
--> irq_handler
--> arch_irq_handler_default
--> asm_do_IRQ
--> generic_handler_irq
- int generic_handle_irq(unsigned
int irq)
- {
- struct irq_desc *desc
= irq_to_desc(irq);
- if (!desc)
- return -EINVAL;
- generic_handle_irq_desc(irq, desc); //封装,继续调用
- return 0;
- }
- EXPORT_SYMBOL_GPL(generic_handle_irq);
--> generic_handler_irq
--> generic_handler_irq_desc
- static inline void generic_handle_irq_desc(unsigned
int irq, struct irq_desc
*desc)
- {
- desc->handle_irq(irq, desc);
//调用每个中断的中断处理函数
- }
以dm9000的eint(7)为例说明一下:
irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
即注册 desc->handle_irq= s3c_irq_demux_eint4_11
3.3 分发外部中断
--> asm_do_IRQ
--> generic_handler_irq
--> generic_handler_irq_desc
--> s3c_irq_demux_eint4_11
在arch/arm/mach-s3c64xx/irq-eint.c中
- static void s3c_irq_demux_eint4_11(unsigned
int irq, struct irq_desc
*desc)
- {
- s3c_irq_demux_eint(4, 11);
- }
--> generic_handler_irq
--> generic_handler_irq_desc
--> s3c_irq_demux_eint4_11
--> s3c_irq_demux_eint
在arch/arm/mach-s3c64xx/irq-eint.c中
- static inline void s3c_irq_demux_eint(unsigned
int start, unsigned
int end)
- {
- u32 status = __raw_readl(S3C64XX_EINT0PEND);
- u32 mask = __raw_readl(S3C64XX_EINT0MASK);
- unsigned int irq;
- status &=
~mask;
- status >>= start;
- status &=
(1 <<
(end
- start + 1))
- 1;
- //当dm9000发生中断时,这儿的status=8=1000b,start=4,end=11
- for (irq
= IRQ_EINT(start); irq
<= IRQ_EINT(end); irq++)
{
- if (status
& 1)
- generic_handle_irq(irq); //irq=IRQ_EINT(7)
- status >>= 1;
- }
- }
IRQ_EINT(7)正好是在dm9000注册的中断号
3.4 第二次调用generic_handle_irq
- int generic_handle_irq(unsigned
int irq)
- {
- struct irq_desc *desc
= irq_to_desc(irq);
- if (!desc)
- return -EINVAL;
- generic_handle_irq_desc(irq, desc);
- return 0;
- }
- static inline void generic_handle_irq_desc(unsigned
int irq, struct irq_desc
*desc)
- {
- desc->handle_irq(irq, desc);
- }
注意,上面的desc->handle_irq也是在 s3c64xx_init_irq_eint() 中注册的,
但这次不一样的,要不成死循环了,这次是 desc->handle_irq= handle_level_irq
三.驱动调用request_irq申请中断
dm9000申请中断:
request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev);
其中: dev->irq=EINT(7)=108, irqflags=0x84=IRQF_SAMPLE_RANDOM|IRQF_SHARED
在include/linux/interrupt.h中
- static inline int __must_check
- request_irq(unsigned
int irq, irq_handler_t handler, unsigned long flags,
- const char
*name, void
*dev)
- {
- return request_threaded_irq(irq, handler,
NULL, flags, name, dev);
- }
在kernel/irq/maage.c中
- int request_threaded_irq(unsigned
int irq, irq_handler_t handler,
- irq_handler_t thread_fn, unsigned long irqflags,
- const char
*devname, void
*dev_id)
- {
- struct irqaction *action;
- struct irq_desc *desc;
- int retval;
- if ((irqflags
& IRQF_SHARED)
&&
!dev_id) //如果是共享中断,缺没有定义dev_id返回错误
- return -EINVAL;
- desc = irq_to_desc(irq);
//根据中断号在irq_desc数组中找到对应的irq_desc结构体
-
- //这个东东在哪初始化的呢?
- if (!irq_settings_can_request(desc))
//!(status_use_accessors & _IRQ_NOREQUEST)
- return -EINVAL;
- if (!handler)
{ //检查有没有指定中断处理函数
- if (!thread_fn)
- return -EINVAL;
- handler = irq_default_primary_handler;
- }
- action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
- action->handler
= handler;
- action->thread_fn
= thread_fn;
- action->flags
= irqflags;
- action->name
= devname;
- action->dev_id
= dev_id;
- chip_bus_lock(desc);
- retval = __setup_irq(irq, desc, action);
- chip_bus_sync_unlock(desc);
- if (retval)
- kfree(action);
- return retval;
- }
附录:
1. s3c6410的外部中断
<s3c6410英文手册_v1.2> section 12.3
s3c6410的外部中断eint0-4,共5个
- NO SOURCES Description Group
- 0 INT_EINT0 External interrupt 0-3 VIC0
- 1 INT_EINT1 External interrupt 4-11 VIC0
- 32 INT_EINT2 External interrupt 12-19 VIC1
- 33 INT_EINT3 External interrupt 20-27 VIC1
- 53 INT_EINT4 External interrupt Group 1-9 VIC1
外部中断4-11会触发1号中断
外部中断12-19会触发32号中断
外部中断20-27会触发33号中断
- XEINT0/GPN0 XEINT1/GPN1 XEINT2/GPN2 XEINT3/GPN3
- XEINT4/GPN4 XEINT5/GPN5 XEINT6/GPN6 XEINT7/GPN7
- XINT8/GPN8 XINT9/GPN9 XEINT10/GPN10 XINT11/GPN11
- XEINT12/GPN12 XEINT13/GPN13 XEINT14/GPN14 XEINT15/GPN15
- EINT16/GPL8 EINT17/GPL9 EINT18/GPL10 EINT19/GPL11
- EINT20/GPL12 EINT21/GPL13 EINT22/GPL14 EINT23/GPM0
- EINT24/GPM1 EINT25/GPM2 EINT26/GPM3 EINT27/GPM4
2. 总结一下s3c6410的中断
- 以IRQ_EINT(7)为例:
- 7+64+5+32
= 108
- 32: 起始偏移
- 64: s3c6410的中断号顺次排列
- 5: 时钟中断
- 7: 外部中断