Linux驱动分析 - 中断
by 宋强
中断控制器驱动层次
Linux中的中断控制器总共分为三层,分别是最上层的驱动接口层,不同种类中断的流程层和硬件相关接口层。
最上层地驱动器接口层
最上层的驱动器接口层主要是用于其他驱动程序例如GPIO、Audio和Video等使用的,是一个最高层的硬件无关实现,具体接口有:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
- irq:全局中断号。
- irq_handler_t 中断处理函数指针,如果这个为NULL并且thread_fn不为NULL,那么会使用牧人的irq_handler_t调用thread_fn作为中断处理函数。
- flags:源代码中找到两重判断,第一层判断是判断是否有共享标志(kernel/irq/manage.c:1814),第二层判断是判断中断的触发形式(kernel/irq/manage.c:1202),比如是边沿触发还是电平触发,所有可选的标志位选项定义在(include/linux/ioport.h:71),IRQF_* 的define。
- name:显示在proc/interrupts中的名字。
- dev:传回给中断处理函数的一个自定义的设备参数,用于在释放的时候区分共享中断的设备,必须唯一。
- 返回值应该是是否出错。
void *free_irq(unsigned int irq, void *dev_id);
- irq:全局中断号。
- dev_id:要释放的指定设备。
- 返回request_irq中传入的设备名字。
当释放中断之后系统会检查是否还有设备使用这个中断,没有的话就会disable这个中断,
(on a shared irq, the caller must ensure the interrupt is disabled on the card it drives before calling this function. 这句不明白。kernel/irq/manage.c:1718)。
这个函数会在所有中断处理函数都返回之后才会返回,所以一定不要在中断处理函数中调用。
void disbale_irq(unsigned int irq);
等待所有失能中断的中断处理函数完成之后失能中断,使能和失能操作会嵌套使能和失能。
可以在中断上下文中使用,但是要非常小心(需要遵循什么条件?)。
void enable_irq(unsigend int irq);
会抵消一次disable_irq产生的影响,只有在满足desc->irq_data.chip->bus_lock还有desc->chip->bus_sync_unlock都为NULL的时候才能在中断函数中调用(暂时不明白)。
disable_irq_nosync() (SMP only);
synchronize_irq() (SMP only);
省略。
int irq_set_irq_type(unsigned int irq, unsigned int type);
用于设置触发模式,可选的type定义在include/linux/irq.h中 IRQ_TYPE_* 变量。
int irq_set_irq_wake(unsigned int irq, unsigned int on);
用于设置此路中断是否能够唤醒睡眠中的系统,第二个参数为enable或disable。
int irq_set_handler_data(unisgned int irq, void *data);
存储自定义数据用的。
int irq_set_chip(unsigned int irq, struct irq_chip *chip);
关联irq号和chip descriptor用。
int irq_set_chip_data(unsigned int irq, void *data);
存储芯片相关自定义数据。
芯片内部的中断使能失能接口
处理器可以通过寄存器设置屏蔽所有的中断,而这样的操作在Linux中是通过local_irq_disable()和local_irq_enable()两个函数完成的。
不同种类中断的流程层
对中断的总结和分类导致了流程层的产生,相同种类的中断处理流程相同,总共有这几种:
- handle_level_irq()
- handle_edge_irq()
- handle_fasteoi_irq()
- handle_simple_irq()
- handle_percpu_irq()
- handle_edge_eoi_irq()
- handle_bad_irq()
系统有默认的可用的处理函数,还不清楚是否需要自己重写。
底层中断控制器封装接口
这个也是我们写中断控制器驱动的话需要实现的接口,所有的接口函数都定义在一个irq_chip结构体中。
一般情况下至少实现irq_ack,然后可能需要实现irq_mask,irq_unmask,irq_set_type还有irq_set_wake等。
具体有关这些函数的说明可以在include/linux/irq.h:400找到。
中断控制器的模型和全局中断号的映射
程序中通过设置irq_chip的parents属性为父中断的中断号的方式来配置中断级联,所以DT中既指定了interrupt-controller又指定了interrupts的这里interrupts里面的中断号就是整个中断控制器级联到父中断控制器上使用的夫中断控制器的中断号。
ARM中的GIC与TI的INTC
armv7以及armv9都是使用的ARM公司设计的GIC作为最上级的中断控制器,但是am33xx系列等TI芯片使用的是TI自己的INTC中断控制器,不过这两个的驱动一般都不用我们关心,和芯片内核紧密相关的驱动都会由上游厂家维护。
tags: Linux - 驱动 - 中断