网站后台程序开发,给家乡做网站,合肥国际网站建设正规平台,邢台专业做wap网站返回目录#xff1a;《ARM-Linux中断系统》。 总结#xff1a;一、二概述了软硬件不同角度的IRQ Number和HW Interrupt ID#xff0c;这就需要他们之间架个桥梁。 三介绍了架设这种桥梁的几种方式#xff1a;Linear、Radix Tree和no map。 四介绍了两种基础数据结构描述中断…返回目录《ARM-Linux中断系统》。 总结一、二概述了软硬件不同角度的IRQ Number和HW Interrupt ID这就需要他们之间架个桥梁。 三介绍了架设这种桥梁的几种方式Linear、Radix Tree和no map。 四介绍了两种基础数据结构描述中断域的irq_domain及针对中断域的操作函数。 五针对中断DeviceTree的个属性进行了解释。 六介绍了从DT到中断映射数据库的过程也即HW interrupt ID到IRQ number之间的映射关系。 七介绍了实际使用中从中断触发时的HW interrupt ID如何映射到IRQ number进而调用中断例程的步骤。 原文地址《Linux kernel的中断子系统之二IRQ Domain介绍》 一、概述 在linux kernel中我们使用下面两个ID来标识一个来自外设的中断 1、IRQ number。CPU需要为每一个外设中断编号我们称之IRQ Number。这个IRQ number是一个虚拟的interrupt ID和硬件无关仅仅是被CPU用来标识一个外设中断。 2、HW interrupt ID。对于interrupt controller而言它收集了多个外设的interrupt request line并向上传递因此interrupt controller需要对外设中断进行编码。Interrupt controller用HW interrupt ID来标识外设的中断。在interrupt controller级联的情况下仅仅用HW interrupt ID已经不能唯一标识一个外设中断还需要知道该HW interrupt ID所属的interrupt controllerHW interrupt ID在不同的Interrupt controller上是会重复编码的。 这样CPU和interrupt controller在标识中断上就有了一些不同的概念但是对于驱动工程师而言我们和CPU视角是一样的我们只希望得到一个IRQ number而不关系具体是那个interrupt controller上的那个HW interrupt ID。这样一个好处是在中断相关的硬件发生变化的时候驱动软件不需要修改。因此linux kernel中的中断子系统需要提供一个将HW interrupt ID映射到IRQ number上来的机制这就是本文主要的内容。 Notes两种视角的中断号IRQ Number从驱动软件来看CPU对每个中断进行编号。HW interrupt ID从中断控制起来看每个中断控制器上的中断都有一个编号。 这两种不同视角就导致了从硬件到软件的一个转换。 二、历史 关于HW interrupt ID映射到IRQ number上 这事在过去系统只有一个interrupt controller的时候还是很简单的中断控制器上实际的HW interrupt line的编号可以直接变成IRQ number。例如我们大家都熟悉的SOC内嵌的interrupt controller这种controller多半有中断状态寄存器这个寄存器可能有64个bit也可能更多每个bit就是一个IRQ number可以直接进行映射。这时候GPIO的中断在中断控制器的状态寄存器中只有一个bit因此所有的GPIO中断只有一个IRQ number在该通用GPIO中断的irq handler中进行deduplex将各个具体的GPIO中断映射到其相应的IRQ number上。如果你是一个足够老的工程师应该是经历过这个阶段的。 随着linux kernel的发展将interrupt controller抽象成irqchip这个概念越来越流行甚至GPIO controller也可以被看出一个interrupt controller chip这样系统中至少有两个中断控制器了一个传统意义的中断控制器一个是GPIO controller type的中断控制器。随着系统复杂度加大外设中断数据增加实际上系统可以需要多个中断控制器进行级联面对这样的趋势linux kernel工程师如何应对答案就是irq domain这个概念。 我们听说过很多的domainpower domainclock domain等等所谓domain就是领域范围的意思也就是说任何的定义出了这个范围就没有意义了。系统中所有的interrupt controller会形成树状结构对于每个interrupt controller都可以连接若干个外设的中断请求我们称之interrupt sourceinterrupt controller会对连接其上的interrupt source根据其在Interrupt controller中物理特性进行编号也就是HW interrupt ID了。但这个编号仅仅限制在本interrupt controller范围内。 Notes中断控制器级联导致多irq domain。HW interrupt ID就只能在本irq domain有效在驱动全局范围内就需要进行不同映射。 三、接口 1、向系统注册irq domain 具体如何进行映射是interrupt controller自己的事情不过有软件架构思想的工程师更愿意对形形色色的interrupt controller进行抽象对如何进行HW interrupt ID到IRQ number映射关系上进行进一步的抽象。因此通用中断处理模块中有一个irq domain的子模块该模块将这种映射关系分成了三类 1线性映射。其实就是一个lookup tableHW interrupt ID作为index通过查表可以获取对应的IRQ number。对于Linear map而言interrupt controller对其HW interrupt ID进行编码的时候要满足一定的条件hw ID不能过大而且ID排列最好是紧密的。对于线性映射其接口API如下 static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node, unsigned int size,该interrupt domain支持多少IRQ const struct irq_domain_ops *ops,callback函数 void *host_data)driver私有数据{ return __irq_domain_add(of_node, size, size, 0, ops, host_data);} 2Radix Tree map。建立一个Radix Tree来维护HW interrupt ID到IRQ number映射关系。HW interrupt ID作为lookup key在Radix Tree检索到IRQ number。如果的确不能满足线性映射的条件可以考虑Radix Tree map。实际上内核中使用Radix Tree map的只有powerPC和MIPS的硬件平台。对于Radix Tree map其接口API如下 static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node, const struct irq_domain_ops *ops, void *host_data){ return __irq_domain_add(of_node, 0, ~0, 0, ops, host_data);} 3no map。有些中断控制器很强可以通过寄存器配置HW interrupt ID而不是由物理连接决定的。例如PowerPC 系统使用的MPIC (Multi-Processor Interrupt Controller)。在这种情况下不需要进行映射我们直接把IRQ number写入HW interrupt ID配置寄存器就OK了这时候生成的HW interrupt ID就是IRQ number也就不需要进行mapping了。对于这种类型的映射其接口API如下 static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, unsigned int max_irq, const struct irq_domain_ops *ops, void *host_data){ return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);} 这类接口的逻辑很简单根据自己的映射类型初始化struct irq_domain中的各个成员调用__irq_domain_add将该irq domain挂入irq_domain_list的全局列表。 2、为irq domain创建映射 上节的内容主要是向系统注册一个irq domain具体HW interrupt ID和IRQ number的映射关系都是空的因此具体各个irq domain如何管理映射所需要的database还是需要建立的。例如对于线性映射的irq domain我们需要建立线性映射的lookup table对于Radix Tree map我们要把那个反应IRQ number和HW interrupt ID的Radix tree建立起来。创建映射有四个接口函数 1调用irq_create_mapping函数建立HW interrupt ID和IRQ number的映射关系。该接口函数以irq domain和HW interrupt ID为参数返回IRQ number这个IRQ number是动态分配的。该函数的原型定义如下 extern unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq); 驱动调用该函数的时候必须提供HW interrupt ID也就是意味着driver知道自己使用的HW interrupt ID而一般情况下HW interrupt ID其实对具体的driver应该是不可见的不过有些场景比较特殊例如GPIO类型的中断它的HW interrupt ID和GPIO有着特定的关系driver知道自己使用那个GPIO也就是知道使用哪一个HW interrupt ID了。 2irq_create_strict_mappings。这个接口函数用来为一组HW interrupt ID建立映射。具体函数的原型定义如下 extern int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base, irq_hw_number_t hwirq_base, int count); 3irq_create_of_mapping。看到函数名字中的ofopen firmware我想你也可以猜到了几分这个接口当然是利用device tree进行映射关系的建立。具体函数的原型定义如下 extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); 通常一个普通设备的device tree node已经描述了足够的中断信息在这种情况下该设备的驱动在初始化的时候可以调用irq_of_parse_and_map这个接口函数进行该device node中和中断相关的内容interrupts和interrupt-parent属性进行分析并建立映射关系具体代码如下 unsigned int irq_of_parse_and_map(struct device_node *dev, int index){ struct of_phandle_args oirq; if (of_irq_parse_one(dev, index, oirq))分析device node中的interrupt相关属性 return 0; return irq_create_of_mapping(oirq);创建映射并返回对应的IRQ number} 对于一个使用Device tree的普通驱动程序我们推荐这样做基本上初始化需要调用irq_of_parse_and_map获取IRQ number然后调用request_threaded_irq申请中断handler。 4irq_create_direct_mapping。这是给no map那种类型的interrupt controller使用的这里不再赘述。 四、数据结构描述 1、irq domain的callback接口 struct irq_domain_ops抽象了一个irq domain的callback函数定义如下 struct irq_domain_ops { int (*match)(struct irq_domain *d, struct device_node *node); int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); void (*unmap)(struct irq_domain *d, unsigned int virq); int (*xlate)(struct irq_domain *d, struct device_node *node, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type);}; 我们先看xlate函数语义是翻译translate的意思那么到底翻译什么呢在DTS文件中各个使用中断的device node会通过一些属性例如interrupts和interrupt-parent属性来提供中断信息给kernel以便kernel可以正确的进行driver的初始化动作。这里interrupts属性所表示的interrupt specifier只能由具体的interrupt controller也就是irq domain来解析。而xlate函数就是将指定的设备node参数上若干个intsize参数中断属性intspec参数翻译成HW interrupt IDout_hwirq参数和trigger类型out_type。 match是判断一个指定的interrupt controllernode参数是否和一个irq domain匹配d参数如果匹配的话返回1。实际上内核中很少定义这个callback函数实际上struct irq_domain中有一个of_node指向了对应的interrupt controller的device node因此如果不提供该函数那么default的匹配函数其实就是判断irq domain的of_node成员是否等于传入的node参数。 map和unmap是操作相反的函数我们描述其中之一就OK了。调用map函数的时机是在创建或者更新HW interrupt IDhw参数和IRQ numbervirq参数关系的时候。其实从发生一个中断到调用该中断的handler仅仅调用一个request_threaded_irq是不够的还需要针对该irq number设定 1设定该IRQ number对应的中断描述符struct irq_desc的irq chip 2设定该IRQ number对应的中断描述符的highlevel irq-events handler 3设定该IRQ number对应的中断描述符的 irq chip data 这些设定不适合由具体的硬件驱动来设定因此在Interrupt controller也就是irq domain的callback函数中设定。 2、irq domain 在内核中irq domain的概念由struct irq_domain表示 struct irq_domain { struct list_head link; const char *name; const struct irq_domain_ops *ops; callback函数 void *host_data; /* Optional data */ struct device_node *of_node; 该interrupt domain对应的interrupt controller的device node struct irq_domain_chip_generic *gc; generic irq chip的概念本文暂不描述 /* reverse map data. The linear map gets appended to the irq_domain */ irq_hw_number_t hwirq_max; 该domain中最大的那个HW interrupt ID unsigned int revmap_direct_max_irq; unsigned int revmap_size; 线性映射的sizefor Radix Tree map和no map该值等于0 struct radix_tree_root revmap_tree; Radix Tree map使用到的radix tree root node unsigned int linear_revmap[]; 线性映射使用的lookup table}; linux内核中所有的irq domain被挂入一个全局链表链表头定义如下 static LIST_HEAD(irq_domain_list); struct irq_domain中的link成员就是挂入这个队列的节点。通过irq_domain_list这个指针可以获取整个系统中HW interrupt ID和IRQ number的mapping DB。host_data定义了底层interrupt controller使用的私有数据和具体的interrupt controller相关对于GIC该指针指向一个struct gic_chip_data数据结构。 对于线性映射 1linear_revmap保存了一个线性的lookup tableindex是HW interrupt IDtable中保存了IRQ number值 2revmap_size等于线性的lookup table的size。 3hwirq_max保存了最大的HW interrupt ID 4revmap_direct_max_irq没有用设定为0。revmap_tree没有用。 对于Radix Tree map 1linear_revmap没有用revmap_size等于0。 2hwirq_max没有用设定为一个最大值。 3revmap_direct_max_irq没有用设定为0。 4revmap_tree指向Radix tree的root node。 五、中断相关的Device Tree知识回顾 想要进行映射首先要了解interrupt controller的拓扑结构。系统中的interrupt controller的拓扑结构以及其interrupt request line的分配情况分配给哪一个具体的外设都在Device Tree Source文件中通过下面的属性给出了描述。这些内容在Device Tree的三份文档中给出了一些描述这里简单总结一下 对于那些产生中断的外设我们需要定义interrupt-parent和interrupts属性 1interrupt-parent。表明该外设的interrupt request line物理的连接到了哪一个中断控制器上 2interrupts。这个属性描述了具体该外设产生的interrupt的细节信息也就是传说中的interrupt specifier。例如HW interrupt ID由该外设的device node中的interrupt-parent指向的interrupt controller解析、interrupt触发类型等。 对于Interrupt controller我们需要定义interrupt-controller和#interrupt-cells的属性 1interrupt-controller。表明该device node就是一个中断控制器 2#interrupt-cells。该中断控制器用多少个cell一个cell就是一个32-bit的单元描述一个外设的interrupt request line。具体每个cell表示什么样的含义由interrupt controller自己定义。 3interrupts和interrupt-parent。对于那些不是root 的interrupt controller其本身也是作为一个产生中断的外设连接到其他的interrupt controller上因此也需要定义interrupts和interrupt-parent的属性。 六、Mapping DB的建立 1、概述 系统中HW interrupt ID和IRQ number的mapping DB是在整个系统初始化的过程中建立起来的过程如下 1DTS文件描述了系统中的interrupt controller以及外设IRQ的拓扑结构在linux kernel启动的时候由bootloader传递给kernel实际传递的是DTB。 2在Device Tree初始化的时候形成了系统内所有的device node的树状结构当然其中包括所有和中断拓扑相关的数据结构所有的interrupt controller的node和使用中断的外设node 3在machine driver初始化的时候会调用of_irq_init函数在该函数中会扫描所有interrupt controller的节点并调用适合的interrupt controller driver进行初始化。毫无疑问初始化需要注意顺序首先初始化root然后first levelsecond level最好是leaf node。在初始化的过程中一般会调用上节中的接口函数向系统增加irq domain。有些interrupt controller会在其driver初始化的过程中创建映射 4在各个driver初始化的过程中创建映射 Notes从最开始的DTB文件到初始化DeviceTree的时候关于中断拓扑数据结构然后在of_irq_init中调用合适的驱动进行初始化最后在驱动初始化中创建映射关系。 2、 interrupt controller初始化的过程中注册irq domain 我们以GIC的代码为例。具体代码在gic_of_init-gic_init_bases中如下 void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node){ irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; ……对于root GIC hwirq_base 16; gic_irqs 系统支持的所有的中断数目16。之所以减去16主要是因为root GIC的015号HW interrupt 是for IPI的因此要去掉。也正因为如此hwirq_base从16开始 irq_base irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());申请gic_irqs个IRQ资源从16号开始搜索IRQ number。由于是root GIC申请的IRQ基本上会从16号开始 gic-domain irq_domain_add_legacy(node, gic_irqs, irq_base, hwirq_base, gic_irq_domain_ops, gic);向系统注册irq domain并创建映射 ……} 很遗憾在GIC的代码中没有调用标准的注册irq domain的接口函数。要了解其背后的原因我们需要回到过去。在旧的linux kernel中ARM体系结构的代码不甚理想。在arch/arm目录充斥了很多board specific的代码其中定义了很多具体设备相关的静态表格这些表格规定了各个device使用的资源当然其中包括IRQ资源。在这种情况下各个外设的IRQ是固定的如果作为驱动程序员的你足够老的话应该记得很长篇幅的针对IRQ number的宏定义也就是说HW interrupt ID和IRQ number的关系是固定的。一旦关系固定我们就可以在interupt controller的代码中创建这些映射关系。具体代码如下 struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, unsigned int size, unsigned int first_irq, irq_hw_number_t first_hwirq, const struct irq_domain_ops *ops, void *host_data){ struct irq_domain *domain; domain __irq_domain_add(of_node, first_hwirq size,注册irq domain first_hwirq size, 0, ops, host_data); if (!domain) return NULL; irq_domain_associate_many(domain, first_irq, first_hwirq, size); 创建映射 return domain;} 这时候对于这个版本的GIC driver而言初始化之后HW interrupt ID和IRQ number的映射关系已经建立保存在线性lookup table中size等于GIC支持的中断数目具体如下 index 015对应的IRQ无效 16号IRQ ------------------16号HW interrupt ID 17号IRQ ------------------17号HW interrupt ID …… 如果想充分发挥Device Tree的威力3.14版本中的GIC 代码需要修改。 3、在各个硬件外设的驱动初始化过程中创建HW interrupt ID和IRQ number的映射关系 我们上面的描述过程中已经提及设备的驱动在初始化的时候可以调用irq_of_parse_and_map这个接口函数进行该device node中和中断相关的内容interrupts和interrupt-parent属性进行分析并建立映射关系具体代码如下 unsigned int irq_of_parse_and_map(struct device_node *dev, int index){ struct of_phandle_args oirq; if (of_irq_parse_one(dev, index, oirq))分析device node中的interrupt相关属性 return 0; return irq_create_of_mapping(oirq);创建映射} 我们再来看看irq_create_of_mapping函数如何创建映射 unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data){ struct irq_domain *domain; irq_hw_number_t hwirq; unsigned int type IRQ_TYPE_NONE; unsigned int virq; domain irq_data-np ? irq_find_host(irq_data-np) : irq_default_domain;A if (!domain) { return 0; } if (domain-ops-xlate NULL)B hwirq irq_data-args[0]; else { if (domain-ops-xlate(domain, irq_data-np, irq_data-args,C irq_data-args_count, hwirq, type)) return 0; } /* Create mapping */ virq irq_create_mapping(domain, hwirq);D if (!virq) return virq; /* Set type if specified and different than the current one */ if (type ! IRQ_TYPE_NONE type ! irq_get_trigger_type(virq)) irq_set_irq_type(virq, type);E return virq;} A这里的代码主要是找到irq domain。这是根据传递进来的参数irq_data的np成员来寻找的具体定义如下 struct of_phandle_args { struct device_node *np;指向了外设对应的interrupt controller的device node int args_count;该外设定义的interrupt相关属性的个数 uint32_t args[MAX_PHANDLE_ARGS];具体的interrupt相当属性的定义}; B如果没有定义xlate函数那么取interrupts属性的第一个cell作为HW interrupt ID。 C解铃还需系铃人interrupts属性最好由interrupt controller也就是irq domain解释。如果xlate函数能够完成属性解析那么将输出参数hwirq和type分别表示HW interrupt ID和interupt type触发方式等。 D解析完了最终还是要调用irq_create_mapping函数来创建HW interrupt ID和IRQ number的映射关系。 E如果有需要调用irq_set_irq_type函数设定trigger type irq_create_mapping函数建立HW interrupt ID和IRQ number的映射关系。该接口函数以irq domain和HW interrupt ID为参数返回IRQ number。具体的代码如下 unsigned int irq_create_mapping(struct irq_domain *domain, irq_hw_number_t hwirq){ unsigned int hint; int virq; 如果映射已经存在那么不需要映射直接返回 virq irq_find_mapping(domain, hwirq); if (virq) { return virq; } hint hwirq % nr_irqs;分配一个IRQ 描述符以及对应的irq number if (hint 0) hint; virq irq_alloc_desc_from(hint, of_node_to_nid(domain-of_node)); if (virq 0) virq irq_alloc_desc_from(1, of_node_to_nid(domain-of_node)); if (virq 0) { pr_debug(- virq allocation failed\n); return 0; } if (irq_domain_associate(domain, virq, hwirq)) {建立mapping irq_free_desc(virq); return 0; } return virq;} 对于分配中断描述符这段代码后续的文章会详细描述。这里简单略过反正指向完这段代码我们就可以或者一个IRQ number以及其对应的中断描述符了。程序注释中没有使用IRQ number而是使用了virtual interrupt number这个术语。virtual interrupt number还是重点理解“virtual”这个词所谓virtual其实就是说和具体的硬件连接没有关系了仅仅是一个number而已。具体建立映射的函数是irq_domain_associate函数代码如下 int irq_domain_associate(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq){ struct irq_data *irq_data irq_get_irq_data(virq); int ret; mutex_lock(irq_domain_mutex); irq_data-hwirq hwirq; irq_data-domain domain; if (domain-ops-map) { ret domain-ops-map(domain, virq, hwirq);调用irq domain的map callback函数 } if (hwirq domain-revmap_size) { domain-linear_revmap[hwirq] virq;填写线性映射lookup table的数据 } else { mutex_lock(revmap_trees_mutex); radix_tree_insert(domain-revmap_tree, hwirq, irq_data);向radix tree插入一个node mutex_unlock(revmap_trees_mutex); } mutex_unlock(irq_domain_mutex); irq_clear_status_flags(virq, IRQ_NOREQUEST); 该IRQ已经可以申请了因此clear相关flag return 0;} 七、将HW interrupt ID转成IRQ number 创建了庞大的HW interrupt ID到IRQ number的mapping DB最终还是要使用。具体的使用场景是在CPU相关的处理函数中程序会读取硬件interrupt ID并转成IRQ number调用对应的irq event handler。在本章中我们以一个级联的GIC系统为例描述转换过程 1、GIC driver初始化 上面已经描述了root GIC的的初始化我们再来看看second GIC的初始化。具体代码在gic_of_init-gic_init_bases中如下 void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node){ irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; ……对于second GIC hwirq_base 32; gic_irqs 系统支持的所有的中断数目32。之所以减去32主要是因为对于second GIC其015号HW interrupt 是for IPI的因此要去掉。而1631号HW interrupt 是for PPI的也要去掉。也正因为如此hwirq_base从32开始 irq_base irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());申请gic_irqs个IRQ资源从16号开始搜索IRQ number。由于是second GIC申请的IRQ基本上会从root GIC申请的最后一个IRQ号1开始 gic-domain irq_domain_add_legacy(node, gic_irqs, irq_base, hwirq_base, gic_irq_domain_ops, gic);向系统注册irq domain并创建映射 ……} second GIC初始化之后该irq domain的HW interrupt ID和IRQ number的映射关系已经建立保存在线性lookup table中size等于GIC支持的中断数目具体如下 index 032对应的IRQ无效 root GIC申请的最后一个IRQ号1 ------------------32号HW interrupt ID root GIC申请的最后一个IRQ号2 ------------------33号HW interrupt ID …… OK我们回到gic的初始化函数对于second GIC还有其他部分的初始化内容 int __init gic_of_init(struct device_node *node, struct device_node *parent){ …… if (parent) { irq irq_of_parse_and_map(node, 0);解析second GIC的interrupts属性并进行mapping返回IRQ number gic_cascade_irq(gic_cnt, irq);设置handler }……} 上面的初始化函数去掉和级联无关的代码。对于root GIC其传入的parent是NULL因此不会执行级联部分的代码。对于second GIC它是作为其parentroot GIC的一个普通的irq source因此也需要注册该IRQ的handler。由此可见非root的GIC的初始化分成了两个部分一部分是作为一个interrupt controller执行和root GIC一样的初始化代码。另外一方面GIC又作为一个普通的interrupt generating device需要象一个普通的设备驱动一样注册其中断handler。 irq_of_parse_and_map函数相信大家已经熟悉了这里不再描述。gic_cascade_irq函数如下 void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq){ if (irq_set_handler_data(irq, gic_data[gic_nr]) ! 0)设置handler data BUG(); irq_set_chained_handler(irq, gic_handle_cascade_irq);设置handler} 2、具体如何在中断处理过程中将HW interrupt ID转成IRQ number 在系统的启动过程中经过了各个interrupt controller以及各个外设驱动的努力整个interrupt系统的database将HW interrupt ID转成IRQ number的数据库这里的数据库不是指SQL lite或者oracle这样通用数据库软件已经建立。一旦发生硬件中断经过CPU architecture相关的中断代码之后会调用irq handler该函数的一般过程如下 1首先找到root interrupt controller对应的irq domain。 2根据HW 寄存器信息和irq domain信息获取HW interrupt ID 3调用irq_find_mapping找到HW interrupt ID对应的irq number 4调用handle_IRQ对于ARM平台来处理该irq number 对于级联的情况过程类似上面的描述但是需要注意的是在步骤4中不是直接调用该IRQ的hander来处理该irq number因为这个irq需要各个interrupt controller level上的解析。举一个简单的二阶级联情况假设系统中有两个interrupt controllerA和BA是root interrupt controllerB连接到A的13号HW interrupt ID上。在B interrupt controller初始化的时候除了初始化它作为interrupt controller的那部分内容还有初始化它作为root interrupt controller A上的一个普通外设这部分的内容。最重要的是调用irq_set_chained_handler设定handler。这样在上面的步骤4的时候就会调用13号HW interrupt ID对应的handler也就是B的handler在该handler中会重复上面的14。 原创文章转发请注明出处。蜗窝科技。http://www.wowotech.net/linux_kenrel/irq-domain.html