Linux IRQ Sub System I

引言

目的

一个合格的linux驱动工程师需要对kernel中的中断子系统有深刻的理解,只有这样,在写具体driver的时候才能:
1)正确的使用linux kernel提供的的API,例如最著名的request_threaded_irq(request_irq)接口
2)正确使用同步机制保护驱动代码中的临界区
3)正确的使用kernel提供的softirq、tasklet、workqueue等机制来完成具体的中断处理

基于上面的原因,我希望能够通过本篇文档来描述清楚linux kernel中的中断子系统方方面面的知识。一方面是整理自己的思绪,另外一方面,抛砖引玉,希望其他同事指出一些不足之处

参考文献

[1]http://www.wowotech.net/irq_subsystem/interrupt_subsystem_architecture.html
Linux kernel的中断子系统之(一):综述
Linux kernel的中断子系统之(二):IRQ Domain介绍
linux kernel的中断子系统之(三):IRQ number和中断描述符
linux kernel的中断子系统之(四):High level irq event handler
Linux kernel中断子系统之(五):驱动申请中断API

Linux kernel的中断子系统之(六):ARM中断处理过程
linux kernel的中断子系统之(七):GIC代码分析
[2] GICv3_Software_Overview_Official_Release_B.pdf
[3] gic_architecture_specification.pdf

约定

1).本篇幅基于MT6853 ANROID R KERHEL-4.14上分析;
2).本篇幅基于GIC-V3,即对应GIC-500的框架上分析;
3).由于gic-v3与v2/v1的差异性过大,便利于后期的更新,不在陈述v2/v1知识点;

综述

中断硬件框架

中断硬件系统主要有三种器件参与:中断源、中断控制器和CPU。
中断源:提供IRQ信号,在发生中断事件的时候,通过电气信号向CPU系统请求处理。
中断源有多样化,比如TP/FP等GPIO、TIMER、UART等等。他们都有自己的寄存器,可以进行相关的设置:是能中断,中断状态,中断类型等等。

中断源太多,CPU需要一个小伙伴帮他,这就是Interrupt controller。
Interrupt Controller:是连中断源和CPU系统的桥梁。可以在中断控制中设置各个中断的优先级;中断控制器会向CPU发出中断信号(简而言之,是通过hwirq向对应的CPU发出IRQ信号),CPU从而通过读取中断控制器对应的寄存器(在GIC 中,读取IAR寄存器),判断当前处理的是哪个中断;中断控制器有多种实现,比如ARM的多数使用GIC(MT6853 GIC-V3);
分发器管理中断源并通过interface向对应的CPU发送中断信号;

CPU:的主要功能是运算,因此CPU并不处理中断优先级,那是Interrupt controller的事情。CPU每执行完一条指令,都会判断是否有异常发生(中断属于异常的一部分);CPU也有自己的寄存器,可以设置使能/禁止中断,这是中断处理总的开关;
对于CPU而言,一般有两种中断请求,例如:对于ARM,是IRQ和FIQ信号线,分别让ARM进入IRQ mode和FIQ mode。对于X86,有可屏蔽中断和不可屏蔽中断。

在这里插入图片描述

中断处理流程

A. CPU每执行完一条指令,都会判断是否有异常发生
B. 发现有中断产生,就开始处理:
.保存现场,即保存数据(寄存器数据)到栈空间里面,或者状态寄存器中;
.分辨中断,调用异常向量列表中中断处理函数;
.恢复现场,从栈空间获取对应数据(寄存器数据);
在这里插入图片描述
接下来重点分析一下IRQ处理流程:
a.异常向量入口:调用对应的中断异常
mt6853_r_tee/kernel-4.14/arch$ vim arm/kernel/entry-armv.S +1210

.L__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)  pc, .L__vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq



/*
 * Vector stubs.
 *
 * This code is copied to 0xffff1000 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not exceed
 * a page size.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
    .macro  vector_stub, name, mode, correction=0
    .align  5
*/	

b. 中断向量: vector_irq,调用对应的__irq_usr和__irq_svc函数

/*
 * Interrupt dispatcher
 */
    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
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f

c. __irq_usr/__irq_svc
__irq_usr:
    usr_entry
    kuser_cmpxchg_check
    irq_handler
    get_thread_info tsk
    mov why, #0
    b   ret_to_user_from_irq
 UNWIND(.fnend      )
ENDPROC(__irq_usr)

__irq_svc:
    svc_entry
    irq_handler

#ifdef CONFIG_PREEMPT
    ldr r8, [tsk, #TI_PREEMPT]      @ get preempt count
    ldr r0, [tsk, #TI_FLAGS]        @ get flags
    teq r8, #0              @ if preempt count != 0
    movne   r0, #0              @ force flags to 0
    tst r0, #_TIF_NEED_RESCHED
    blne    svc_preempt
#endif

    svc_exit r5, irq = 1            @ return from exception
 UNWIND(.fnend      )
ENDPROC(__irq_svc)

可见,
这2个函数的处理过程类似:
保存现场
调用 irq_handler
恢复现场

d. irq_handler: 将会调用C函数 handle_arch_irq

/*
 * Interrupt handling.
 */
    .macro  irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr r1, =handle_arch_irq
    mov r0, sp
    badr    lr, 9997f
    ldr pc, [r1]
#else
    arch_irq_handler_default
#endif


e. handle_arch_irq的处理过程
读取寄存器获得中断信息: hwirq
把hwirq转换为virq
调用 irq_desc[virq].handle_irq

通过 handle_irq 调用action量表里面我们申请中断时的处理函数;

可见,
中断结构如下:
中断源(比如GPIO) --> 中断控制器(sub int controller —> int controller) —> cpu

发生中断时,
cpu跳到"vector_irq", 保存现场, 调用C函数handle_arch_irq
handle_arch_irq:
a. 读 int controller, 得到hwirq
b. 根据hwirq得到virq
c. 调用 irq_desc[virq].handle_irq

如果该中断没有子中断, irq_desc[virq].handle_irq的操作:
a. 取出irq_desc[virq].action链表中的每一个handler, 执行它
b. 使用irq_desc[virq].irq_data.chip的函数清中断

如果该中断是有子中断产生, irq_desc[virq].handle_irq的操作:
a. 读 sub int controller, 得到hwirq’
b. 根据hwirq’得到virq
c. 调用 irq_desc[virq].handle_irq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人在路上……

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值