platform: imx6dl
os: Android 4.4
kernel branch: 3.0.35
初始化:
start_kernel -> main.c
early_irq_init irqdesc.c //没有定义CONFIG_SPARSE_IRQ,使用的是静态分配irq_desc,并且初始化。
init_IRQ -> irq.c
machine_desc->init_irq -> //调用平台相关的init_irq
mx6_init_irq ->
mx6q_register_gpios -> devices.c
mxc_gpio_init -> //一方面注册gpio,一方面配置对应的irq
irq_set_chip_and_handler -> //设置对应的irq chip以及handler, handler为 handle_level_irq,这里申请的是非级联的中断,系统起来后可以被申请的。
irq_set_chip_and_handler_name ->
irq_set_chip
__irq_set_handler
irq_set_chained_handler -> //这里设置的是级联的irq,比如gpio 0 ~ 15都是通过此node来进入系统的,此类irq不能再request,而且也不能创建kernel thread。对应handler是 mx3_gpio_irq_handler
__irq_set_handler ->
irq_settings_set_norequest //不能request
irq_settings_set_nothread //不能有thread
中断触发:
__irq_svc或者__irq_usr -> entry-armv.S, 两者区别在于是内核空间还是用户空间触发的中断。
irq_handler ->
arch_irq_handler_default -> entry-macro-multi.S //没有定义CONFIG_MULTI_IRQ_HANDLER,执行默认中断处理
asm_do_IRQ -> irq.c //参数里已经有了irq number。
handle_IRQ ->
irq_enter
generic_handle_irq ->
generic_handle_irq_desc ->
desc->handle_irq -> 先调用的是级联中断控制器的处理函数,即前面的mx3_gpio_irq_handler。
mx3_gpio_irq_handler -> gpio.c 得到具体的irq number号
generic_handle_irq -> 再次调用
generic_handle_irq_desc ->
desc->handle_irq -> 这次调用的是非级联中断控制器的处理函数,即前面的handle_level_irq
handle_level_irq -> chip.c
handle_irq_event -> handle.c
handle_irq_event_percpu ->
action->handler 如果有的话调用上半部中断处理程序
irq_wake_thread -> 如果有的话唤醒内核中断线程
wake_up_process
irq_exit ->
invoke_softirq -> softirq.c //处理软中断,tasklet也是softirq的一种,也会走这个流程。此时硬件中断已经打开。
__do_softirq
小结:
1. 首次申请中断时,默认情况中断是打开时能的。
2. 中断上半部要使用disable_irq_nosync()而不是disable_irq(),否则会导致死锁。
3. disable_irq()/enable_irq()可以多次嵌套调用,但是一定要在数量上配对。
4. 执行中断上半部时当前中断线是被屏蔽的,包括其他cpu,当前其他irq number不会被屏蔽。
5. 一个软中断不会抢占另一个软中断,唯一可抢占它的只有中断处理程序,因为在处理软中断处理程序的时候,
中断是开启的,但它和中断处理程序一样不能休眠。本地软中断被禁止,但是其他处理器上可执行软中断,
甚至是同类的中断,那么处理函数就会被执行两次,数据就会遭到破坏了。因此如果没必要,我们就用tasklet,
它的同一个处理函数不会在两个处理器上同时运行,这样也就避免了加锁的麻烦。
参考: