之前在做spi、iic等虚拟设备驱动时,就在思考如何开发虚拟的irq controller驱动,因为完成虚拟irqcontroller驱动后,进行虚拟外设驱动开发时,就可以使用irq。因此这周就简要介绍irq子系统框架,然后再开发一个虚拟的irq controller driver 。
针对irq 子系统专栏,主要包括如下两部分内容:
一、IRQ子系统框架简述
二、虚拟IRQ CONTROLLER DRIVER实例
本篇主要是IRQ子系统框架简述,本篇文章主要分为如下几个小章节:
一、IRQ相关概念说明
二、IRQ子系统相关数据结构及其关联说明
三、IRQ子系统提供的相关接口
对于irq子系统,我也就是对IRQ子系统的实现有所了解,并基于对mips框架的的中断处理有一点了解,对于arm架构的GIC则不熟悉。本专栏的内容也可能存在大量的漏洞,还请大家斧正。
一、IRQ相关概念说明
针对cpu而言,其提供硬件中断,而外设则可以通过硬件中断线来中断系统。和clk、regulator子系统类似,在soc中也可以级联多个irq controller,实现irq 的扩展。
上图就是soc中存在3种irq controller的级联,而内核IRQ子系统则通过抽象对应的数据结构,实现在一片soc中支持多个irq controller。
针对每一个irq controller而言,其本身的中断号基本上也是从0开始,因此针对一个系统中存在多个irq controller而言,需要识别不同控制器的相同中断号信息。
二、IRQ子系统相关数据结构及其关联说明
下面我们通过IRQ子系统提供的数据结构,说明IRQ子系统的框架。以便我们能够快速理解IRQ子系统的实现,并理解如何实现irq controller driver。针对一个irq controller而言,一个irq controller可以包含多个hw irq line,而一个hw irq line上可以由多个外设共用(共享中断),因此针对一个hw irq line,需支持提供多个action,实现针对不同外设的中断响应。
另外针对每一个irq controller而言,其本身的中断号基本上也是从0开始,因此针对一个系统中存在多个irq controller而言,需要识别不同控制器的相同中断号信息,而在LINUX子系统中针对系统中所有存在的hw irq line,均为其分配logic irq index,且logic irq index是系统唯一的,因此需要提供一种map机制,实现logic irq index与hw irq line的映射。另外针对一个irq controller的所有hw irq line而言,针对该hw irq line的enable、disable。
基于此,针对IRQ子系统的数据结构大致可分为如下三类:
一、 struct irq_domain,用于描述irq域,该数据结构主要用于存储hw irq line与logic irq index的映射关系,并提供irq domain ops,实现该irq域下hw irq line的top handler的设置等操作;
二、hw irq line的抽象,主要是struct irq desc,包括该hw irq line中断触发时,值行的对应操作,包括top handler(该hw irq line的顶层处理接口),以及使用该irq的外设注册的操作接口(当一个hw irq line是共享中断时,可注册多个操作接口)等等信息;
三、irq controller的操作接口,主要是数据结构struct irq_chip,提供中断的使能与否接口等。
基本上就是这三类。其中:
struct irq_chip用于操作irq controller,实现hw irq line的使能、去使能、中断触发类型等操作;
struct irq_desc,主要用于hw irq line触发后的处理操作接口的注册等,而且该struct irq_desc是IRQ子系统的最主要数据结构,其连接了irq_chip、irq_domain数据结构;
而struct irq_domain主要用于实现logic irq index与hw irq index的映射关系,以便根据logic irq index,即可找到该irq_domain下对应的hw irq index。struct irq_domain相关定义说明
如下即是struct irq_domain的定义,系统中所有的struct irq_domain均加入到链表irq_domain_list中。
struct irq_domain主要实现如下几个功能:
存储该irq controller的所有hw irq line与对应的logic irq inex的映射关系,而这种映射关系主要包括如下三种类型:
若irq controller支持修改hw irq line的index,则无需存储hw irq line与对应的logic irq inex的映射关系,只需设置hw irq line与对应的logic irq index相同即可;
若该irq controller支持的irq个数较小,则直接实现linear map,即使用数组存储hw irq line与logic irq index;
若该irq controller支持的irq个数较多,则使用基数树存储hw irq line与logic irq index的映射。Irq domain也定义了操作接口,其中xlate则主要是获取hw irq line与对应的logic irq index,而map接口则主要用于设置irq desc与irq_chip的关联,同时完成irq desc与top handler的关联。
struct irq desc与struct irq_chip相关关联说明
如下所示,struct irq_desc与struct irq_chip、struct irq_data、struct irqaction。其中struct irq_chip主要定义了一个irq controller的操作接口,包含enable、disable、unmask、mask等接口等。而struct irq_data则主要用于存储struct irq_desc的数据信息,包含该irq所属的irq controller的操作接口、所属的irq domain、hw irq line与logic irq index等,而这些关联一般通过调用irq_domain->map接口实现设置(包括irq_desc与irq_chip关联、该irq_desc的Interrupt flow handler的设置)。
irq_desc的存储方式
以上就是IRQ子系统相关的数据结构间的定义及关联。而针对irq_desc的存储,存在两种不同的存储方式,若系统不支持通过基数树存储系统中存在的irq_desc,则主要使用数组存储,如下图所示:
显然使用数组存储的话,不管系统中是否真正使用了所有的中断,均需要为其定义内存空间,因此也就提供了基于基数树的存储方式,基于该存储方式,仅在创建一个irq_desc时方才为其申请内存空间,并存储至基数树中,其关联如下图所示
中断处理流程
而下图即是硬件中断触发后,中断处理函数执行的流程。主要包含如下几个方面:
各架构的芯片接收到硬件中断后,最终均会调用do_IRQ接口,进行硬件中断的处理操作;
do_IRQ接口则调用generic_handle_irq接口进行处理;
而在generic_handle_irq接口中,则通过irq获取该irq对应的irq_desc,获取该irq的Interrupt flow handler(如针对电平触发的中断一般注册为handle_level_irq,而针对边缘触发的中断,则注册为handle_edge_irq等);
而在irq的Interrupt flow handler接口中,则执行所有注册到irq_desc上的handler接口(即通过request_irq等接口注册的中断处理函数)
三、IRQ子系统提供的相关接口
irq子系统提供的接口主要包括两部分:
一、用于irq controller dirver使用的接口 irq_alloc_descs(申请irq desc的接口)
通过调用irq_alloc_descs申请一个irq对应的struct irq_desc(若是使用数组存储struct irq_desc,则该接口直接返回对该irq_desc在数组中地址即可;若使用基数树存储,则需要为该irq_desc申请内存,并加入到基数树中);
Irq domain创建的接口
主要有irq_domain_add_legacy、irq_domain_add_simple、irq_domain_add_linear、irq_domain_add_tree、irq_domain_add_nomap,其中irq_domain_add_legacy、irq_domain_add_simple主要是选择linear存储irq domain中hw irq line与logic irq index的映射关系。
Irq_desc与irq_chip的绑定接口
主要是接口irq_set_chip_and_handler_name、irq_set_handler、irq_set_chip接口。
二、用于外设申请irq的接口
主要是request_irq、request_threaded_irq接口等,而对于大多数驱动工程师而言,基本上只需要使用该接口即可完成中断处理函数的注册。
以上就是IRQ子系统的简述内容,其实对于大多数驱动工程师而言,只需要会使用中断处理函数的注册即可,无须太关注IRQ controller driver的实现。但我们了解一些irq controller driver的开发流程,对于我们以后开发驱动也是有所帮助的。比如我们需要实现一个gpio controller driver驱动,而该gpio controller支持gpio中断,硬件上是将该gpio的中断输出脚连接到一个系统中断上,那么我们就可以为该gpio注册一个irq domain,并为该gpio controller的各引脚注册对应的irq desc,同时还需要定义irq_chip实现中断使能相关的操作接口等。因此知道IRQ子系统实现以及irq controller driver的实现也有助于我们开发这一类的驱动。