在上一篇文章中,我们简要分析了中断子系统框架,本章我们将通过实现一个虚拟的中断控制器驱动,说明如何进行中断控制器驱动开发。本章的内容大概分为如下几部分:

一、实现虚拟中断控制器的可行性说明

二、中断控制器驱动的开发流程

三、虚拟中断控制器驱动设计说明

一、实现虚拟中断控制器的可行性说明

在进行虚拟中断控制器的设计之前,我们先说明下LINUX系统中硬件中断触发后的处理过程。如下图所示,当硬件中断触发后,各架构的CPU板级代码经过处理后,大都会调用do_IRQ/generic_handle_irq接口,从而调用具体irq_desc的irq flow handler接口,进行中断处理函数的调用等工作。基于此,我们可以提供操作接口调用generic_handle_irq,从而模拟硬件中断触发,实现中断处理流程的触发操作,这是我们实现虚拟中断控制器的理论基础。

linux系统常见的中断(中断子系统专栏之二)(1)

二、中断控制器驱动的开发流程

在提供了模拟硬件中断触发的机制后,我们再确认下中断控制器驱动的开发流程,主要分为如下几个部分:

  1. 为该中断控制器的所有hw irq line,均创建对应的irq_desc(因为我是在ubuntu16.04上开发的虚拟中段控制器驱动,并没有提供设备树支持,因此在驱动开发时完成了所有irq_desc的申请,如使用设备树开发的话,则无须申请所有的irq_desc。当有外设用到其中的一个hw irq line时,则在该设备的设备节点中引用对应的hw irq index及该hw irq index对应controller的设备节点名即可,然后在外设的驱动中调用irq_of_parse_and_map接口即可(而在该接口中若检测到该hw irq line尚未创建irq_desc,则进行irq_desc的创建,同时在该hw irq所属的irq domain中,完成hw irq index与logic irq index的映射)。设备树这种方式确实很好,仅在hw irq被使用时方进行irq_desc的创建及注册操作);
  2. 为该irq controller 提供操作接口,即完成struct irq_chip类型变量的定义及赋值,主要包括中断的使能控制、中断触发方式的设置等等;
  3. 为该中断控制器创建irq domain,以便存储该irq controller的hw irq line与logic irq index的映射关系,并完成该irq_desc与irq_chip、irq flow handler的map操作操作等。
三、虚拟中断控制器驱动设计说明

有了以上的基础后,我们即可以进行虚拟中断控制器驱动的开发,以便了解中断控制器驱动的开发流程。

虚拟中断控制器型号介绍与说明

本次我们开发的虚拟中断控制器包含两个型号virt0808、virt0813,这两个中断控制器均提供16个hw irq line。然后这两个中断控制器级联在一起(virt0813连接至virt0808的第16个中断号,virt0808作为root controller),连接方式如下图所示。

linux系统常见的中断(中断子系统专栏之二)(2)

这两个中断控制器均提供如下几个寄存器:

  1. 中断状态寄存器(status reg);
  2. 中断使能寄存器(mask reg);
  3. 中断电平触发控制寄存器(level reg);
  4. 中断边沿触发控制寄存器(edeg reg)

以上即为虚拟中断控制器的主要内容,下面为这两个中断控制器进行驱动开发。

虚拟中断控制器驱动开发设计

针对虚拟中断控制器驱动,主要包括如下几步部分内容:

  1. 提供virt0808虚拟中断控制器驱动,并在sysfs目录下创建属性文件irq_trigger,供应用程序模拟触发硬件中断;
  2. 提供外设驱动,申请中断,作为对虚拟中断的验证。
相关的数据结构

针对virt0808,定义数据结构virt_irq_provider,主要的成员包括:

  1. 存储应用程序设置的模拟触发中断的hw irq line index(irq_trigger_value);
  2. 定义struct irq_work类型的变量work,主要用于触发irq中断使用;
  3. 该irq控制器的logic irq base id,及hw irq line的个数;
  4. 该irq controller的寄存器;
  5. 该irq controller对应的irq_chip、irq_domain;
  6. 该irq controller driver所对应的platform device。

linux系统常见的中断(中断子系统专栏之二)(3)

针对模拟硬件中断触发,我们主要借助irq work实现。主要的调用流程如下图所示

  1. 当应用程序通过向irq_trigger写入需要模拟触发的hw irq line后,则会调用virt0808 driver定义的函数virt0808_irq_store,在该函数中调用irq_work_queue,将该irq work加入irq work queue中,并触发irq work interrupt;
  2. 而在irq work interrupt的处理函数中调用irq_work_run,处理virt0808 driver定义的处理函数,即virt0808_hwirq_trigger;
  3. 在virt0808_hwirq_trigger接口中则调用generic_handle_irq接口中,则调用该irq controller对应hw irq line的irq_desc进行中断的处理操作

linux系统常见的中断(中断子系统专栏之二)(4)

而针对virt0813而言,其数据结构定义如下,该数据结构的定义与virt_irq_provider的定义类似。

linux系统常见的中断(中断子系统专栏之二)(5)

Virt0808 driver

linux系统常见的中断(中断子系统专栏之二)(6)

在进行irq domain的map接口中完成irq desc与irq chip、irq flow handler的绑定操作

linux系统常见的中断(中断子系统专栏之二)(7)

在probe接口中通过调用如下接口完成irq domain、irq desc的创建操作

linux系统常见的中断(中断子系统专栏之二)(8)

以上就是virt0808的驱动程序,完成以上功能后,就完成针对virt0808 irq controller的驱动操作。

Virt0813 driver

该设备驱动与virt0808 driver的驱动类似,唯一区别的是,因为virt0813是挂载在virt0808上的,因此当virt0813上中断发生时,cpu首先得到的是virt0808对应引脚中断,然后在virt0808的chain hw irq line的中断处理函数中,通过读取virt0813的中断状态寄存器,确认virt0813上是哪一个hw irq line发生中断,再调用generic_handle_irq接口去处理该中断。处理过程如下图所示:

linux系统常见的中断(中断子系统专栏之二)(9)

virt0813_sub_irq_handler的定义如下:

linux系统常见的中断(中断子系统专栏之二)(10)

测试驱动

在测试驱动中主要申请两个irq,一个irq是virt0808 irq controller的hw irq line;一个irq是virt0813 irq controller 的hw irq line,该驱动仅仅是测试用,比较简单,代码如下:

linux系统常见的中断(中断子系统专栏之二)(11)

测试验证

在该virt irq的目录下执行make &&make install,驱动镜像即在image目录下。

驱动加载:

insmod virt0808_dev.ko

insmod virt0808_irq.ko

insmod virt0813_irq.ko

insmod irq_consumer_dev.ko

insmod irq_consumer_driver.ko

Virt0808 irq controller中断触发验证

因我们在测试驱动中,申请了virt0808的第7个中断,因此我们触发该中断,看下该中断对应的中断处理程序有没有调用:

cd /sys/devices/platform/virt0808_irq_dev.0/

echo 6 >>irq_trigger

dmesg

linux系统常见的中断(中断子系统专栏之二)(12)

如上图所示,中断处理函数已经执行。下面我们进行virt0813中断的测试,我们申请的是virt0813的第一个中断,执行

命令如下:

cd /sys/devices/platform/virt0813_irq_dev.0

echo 1 >>irq_status

cd /sys/devices/platform/virt0808_irq_dev.0/

echo 15 >>irq_trigger

dmesg

linux系统常见的中断(中断子系统专栏之二)(13)

如上图所示,中断处理函数也已经执行成功,测试成功。

以上就是本篇文章的主要内容,我们主要实现了virt irq controller driver,熟悉irq controller driver的开发流程,我们实现的驱动中,两个irq controller通过级联的方式连接,也比较有代表性。我们学习了irq子系统下irq controller driver的开发后,对我们平常的开发有没有帮助呢?当我们需要为一个gpio controller开发驱动,且该gpio controller支持中断时,则可以为该gpio controller创建irq controller driver即可完成该功能(此种功能需求下,可参考virt0813的驱动程序实现该需求)。至此我们完成了irq子系统的学习,针对irq子系统我也就是会开发相应驱动,若要彻底理解中断子系统,还有很长的路要走…后续如果有可能,会再深入学习(本次虚拟驱动的源码链接稍后会发出来,或者在gitee上搜索用户jerry_chg,也可以找到本次的驱动代码)。

,