• 了解IRQ Domain(中断控制器)

1.如何理解中断号?

  每个IRQ同时有"irq"和"hwirq"两个编号。

  • "hwirq"是硬件中断号(物理中断号),即芯片手册上写的号码,Interrupt controller用hwirq来标识外设中断。在interrupt controller级联的情况下,仅仅用hwirq已经不能唯一标识一个外设中断,还需要知道该HW interrupt ID所属的interrupt controller(HW interrupt ID在不同的Interrupt controller上是会重复编码的)。
  • "irq"是软件使用的中断号,或者说是逻辑/虚拟中断号。CPU需要为每一个外设中断编号,它是一个虚拟interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断。

  如果芯片中有多个中断控制器,假设它们各自都对应16个IRQ,这16个IRQ的编号都是从0到15,那CPU收到中断后,软件单凭中断号是分不清这个中断到底是从哪个中断控制器派发过来的,它需要的是中断控制器自身的编号加上IRQ的编号。

2.IRQ Domain

  早期的系统,只有一个中断控制器,接入中断控制器的物理中断号都是不同的。但是随着计算机系统的发展,系统中可以挂接更多的中断控制器。特别是嵌入式系统的出现,类似GPIO这种也可以视作一种中断控制器。每个中断控制器都有自己中断线的物理编号,且这些物理编号会有重复。此时,Linux Kernel发展出了IRQ Domain的概念,来区分这些相同的物理中断编号。

  IRQ Domain,顾名思义,即中断控制域。每个中断控制器都有自己的struct irq_domain结构体,以及自己下属的物理中断号,也不用担心物理中断号重复无法区分的问题。以下图为例,IRQ Controller 1及其下属的几根中断输入线作为一个中断控制域,而IRQ Controller 2作为另外一个中断控制域。

  对于驱动工程师而言,只希望得到一个IRQ number,而不关系具体是哪个interrupt controller上的哪个HW interrupt ID。因此kernel中的中断子系统需要提供一个将HW interrupt ID映射到IRQ number上来的机制。

  从kernel的角度来看,任何外部设备的中断都是一个异步事件,kernel都需要识别这个事件。内核用IRQ number来标识某一个设备的某个interrupt request。有了IRQ number就可以定位到该中断的描述符(struct irq_desc)。但是,对于中断控制器而言,它不并知道IRQ number,它只是知道HW interrupt number(中断控制器会为其支持的interrupt source进行编码,这个编码被称为Hardware interrupt number )。不同的软件模块用不同的ID来识别interrupt source,这样就需要映射了。如何将Hardware interrupt number 映射到IRQ number呢?这需要一个translation object,因此irq domain应用而生

2.1.结构体

1).struct irq_domain(抽象为中断控制器)

struct irq_domain { struct list_head link;    //用于将irq_domain连接到全局链表irq_domain_list中const char *name;         //中断控制器名称const struct irq_domain_ops *ops; //irq domain映射操作使用的方法集合void *host_data;        //指向一个struct gic_chip_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;            //该irq domain支持中断数量的最大值。 unsigned int revmap_direct_max_irq; unsigned int revmap_size;               //线性映射的大小struct radix_tree_root revmap_tree;     //Radix Tree map使用到的radix tree root node unsigned int linear_revmap[];           //线性映射使用的lookup table
};

  在初始化中断控制器时,解析DTS信息中定义了几个中断控制器,每个中断控制器都注册一个struct irq_domain数据结构

  对GIC中断控制器而言,host_data成员指向一个struct gic_chip_data的数据结构,该结构体通过gic_init_bases(drivers/irqchip/irq-gic-v3.c)初始化。

2).struct gic_chip_data

    50 struct gic_chip_data {                                                                                51     struct fwnode_handle    *fwnode;52     void __iomem        *dist_base;53     struct redist_region    *redist_regions;54     struct rdists       rdists;55     struct irq_domain   *domain;56     u64         redist_stride;57     u32         nr_redist_regions;58     bool            has_rss;59     unsigned int        irq_nr;60     struct partition_desc   *ppi_descs[16];61 };

  在注册GIC irq domain时还有一个重要的数据结构irq_domain_ops,对于GIC,其irq domain的操作函数是gic_irq_domain_ops,定义如下:

  1036 static const struct irq_domain_ops gic_irq_domain_ops = {                                             1037     .translate = gic_irq_domain_translate,1038     .alloc = gic_irq_domain_alloc,1039     .free = gic_irq_domain_free,1040     .select = gic_irq_domain_select,1041 };

3).struct irq_domain_ops:

struct irq_domain_ops {
//如果irq_domain不是HIERARCHY的,那么使用下面的函数int (*match)(struct irq_domain *d, struct device_node *node,enum irq_domain_bus_token bus_token);int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);----使hwirq映射到virqvoid (*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);-----------------得到hwirq//相反,如果是HIERARCHY的,那么使用下面的函数
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY/* extended V2 interfaces to support hierarchy irq_domains */int (*alloc)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs, void *arg);                    //同map的作用void (*free)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs);void (*activate)(struct irq_domain *d, struct irq_data *irq_data);void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,unsigned long *out_hwirq, unsigned int *out_type);        //同unmap的作用
#endif
};

CONFIG_IRQ_DOMAIN_HIERARCHY:
  We plan to use hierarchy irqdomain to suppport CPU vector assignment, interrupt remapping controller, IO-APIC controller, MSI interrupt and hypertransport interrupt etc on x86 platforms. So extend irqdomain interfaces to support hierarchy irqdomain. There are already many clients of current irqdomain interfaces. To minimize the changes, we choose to introduce new version 2 interfaces to support hierarchy instead of extending existing irqdomain interfaces.
  According to Thomas’s suggestion, the most important design decision is to build hierarchy struct irq_data to support hierarchy irqdomain, so hierarchy irqdomain related data could be saved in struct irq_data.With support of hierarchy irq_data, we could also support stacked irq_chips. This is most useful in case of set_affinity().

  • 非层次函数接口

    • xlate函数,在DTS文件中,各个使用中断的device node会通过一些属性(例如interrupts和interrupt-parent属性)来提供中断信息给kernel以便kernel可以正确的进行driver的初始化动作。这里,interrupts属性所表示的interrupt specifier只能由具体的interrupt controller(也就是irq domain)来解析。而xlate函数就是将指定的设备(node参数)上若干个(intsize参数)中断属性(intspec参数)翻译成HW interrupt ID(out_hwirq参数)和trigger类型(out_type)。

    • match是判断一个指定的interrupt controller(node参数)是否和一个irq domain匹配(d参数),如果匹配的话,返回1。实际上,内核中很少定义这个callback函数,实际上struct irq_domain中有一个of_node指向了对应的interrupt controller的device node,因此,如果不提供该函数,那么default的匹配函数其实就是判断irq domain的of_node成员是否等于传入的node参数。

    • map和unmap,irq_domain_associate()调用map函数。

  这些设定不适合由具体的硬件驱动来设定,因此在Interrupt controller,也就是irq domain的callback函数中设定。

2.2.GIC代码分析

2.2.1.gic_init_bases()

 drivers/irqchip/irq-gic-v3.c:1071 static int __init gic_init_bases(void __iomem *dist_base,1072                  struct redist_region *rdist_regs,1073                  u32 nr_redist_regions,1074                  u64 redist_stride,1075                  struct fwnode_handle *handle)1076 {1077     u32 typer;1078     int gic_irqs;1079     int err;10801081    //初始化struct gic_chip_data gic_data;1087     gic_data.fwnode = handle;1088     gic_data.dist_base = dist_base;1089     gic_data.redist_regions = rdist_regs;1090     gic_data.nr_redist_regions = nr_redist_regions;1091     gic_data.redist_stride = redist_stride;1092 1093     /*1094      * Find out how many interrupts are supported.1095      * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)1096      */1097     typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);1098     gic_data.rdists.gicd_typer = typer;1099     gic_irqs = GICD_TYPER_IRQS(typer);1100     if (gic_irqs > 1020)1101         gic_irqs = 1020;1102     gic_data.irq_nr = gic_irqs;1103 1104     gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,&gic_data);1106     irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);1107     gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));1108     gic_data.rdists.has_vlpis = true;1109     gic_data.rdists.has_direct_lpi = true;1116     gic_data.has_rss = !!(typer & GICD_TYPER_RSS);1119 1120     if (typer & GICD_TYPER_MBIS) {1121         err = mbi_init(handle, gic_data.domain);1122         if (err)1123             pr_err("Failed to initialize MBIs\n");1124     }1125                                                                    1126     set_handle_irq(gic_handle_irq);1128     gic_update_vlpi_properties();1129 1130     if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())1131         its_init(handle, &gic_data.rdists, gic_data.domain);1132 1133     gic_smp_init();1134     gic_dist_init();1135     gic_cpu_init();1136     gic_cpu_pm_init();1138     return 0;1145 }

  分配一个irq domain的数据结构,并调用irq_domain_create_tree进行注册,添加到链表irq_domain_list。每个interrupt controller都会填充为一个irq domain,负责解析其下游的interrut source。如果interrupt controller有级联的情况,那么一个非root interrupt controller的中断控制器也是其parent irq domain的一个普通的interrupt source。

  gic_init_bases函数重点分析:

  • 确认支持SPI 中断号最大的值为多少,GICv3最多支持1020个中断(SPI+SGI+SPI).GICD_TYPER寄存器bit[4:0], 如果该字段的值为N,则最大SPI INTID为32(N + 1)-1。 例如,0x00011指定最大SPI INTID为127。

  • 调用irq_domain_create_tree 向系统中注册一个irq domain的数据结构,该函数的重要两个参数为gic_irq_domain_ops,gic_data。irq_domain主要作用是将硬件中断号映射到IRQ number。

  • 用于区分MSI域。 比如一个域用作PCI/MSI, 一个域用作wired IRQS.

  • 判断GICD 是否支持rss, rss(Range Selector Support)表示SGI中断亲和性的范围 GICD_TYPER寄存器bit[26], 如果该字段为0,表示中断路由(IRI) 支持affinity 0-15的SGI,如果该字段为1, 表示支持affinity 0 - 255的SGI

  • 判断是否支持通过写GICD寄存器生成消息中断。GICD_TYPER寄存器bit[16]

  • 设置handle_arch_irq为gic_handle_irq。在kernel发生中断后,会跳转到汇编代码entry-armv.S中__irq_svc处,进而调用handle_arch_irq,从而进入GIC驱动,进行后续的中断处理。

  • 更新vlpi相关配置。gic虚拟化相关。

  • 初始化ITS。 Interrupt Translation Service, 用来解析LPI中断。 初始化之前需要先判断GIC是否支持LPI,该功能在ARM里是可选的。

  • gic_smp_init主要包含两个作用。

    • 触发SGI中断,用于CPU之间通信。当一个CPU core上的软件控制行为需要传递到其他的CPU上的时候,就会调用这个callback函数(例如在某一个CPU上运行的进程调用了系统调用进行reboot)。对于GIC v3,这个callback定义为gic_raise_softirq.
    • 设置CPU 上下线流程中和GIC相关的状态机。
  • 初始化GICD。

  • 初始化CPU interface.

  • 初始化GIC电源管理。

2.2.2.__irq_domain_add()

  第1104行函数irq_domain_create_tree调用__irq_domain_add,分配并初始化struct irq_domain,并将创建好的struct irq_domain加入全局链表irq_domain_list

struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,irq_hw_number_t hwirq_max, int direct_max,const struct irq_domain_ops *ops,void *host_data)
{struct device_node *of_node = to_of_node(fwnode);struct irq_domain *domain;domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),GFP_KERNEL, of_node_to_nid(of_node));of_node_get(of_node);/* Fill structure */INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);domain->ops = ops;domain->host_data = host_data;domain->fwnode = fwnode;domain->hwirq_max = hwirq_max;domain->revmap_size = size;domain->revmap_direct_max_irq = direct_max;irq_domain_check_hierarchy(domain);mutex_lock(&irq_domain_mutex);list_add(&domain->link, &irq_domain_list);mutex_unlock(&irq_domain_mutex);return domain;
}

3.Types of irq_domain mappings

  注册中断处理函数是唯一的中断号是 Kernel分配的全局唯一虚拟中断号,Linux通过一定方式将其和中断控制域的物理中断号关联。对每个中断控制域来讲,这种映射/关联主要有以下三类:

  • 线性映射(Linear):固定大小的数组映射(物理中断号为数组索引)。当该中断控制域的物理中断号较连续且数量不大时使用。
  • 基树映射(Radix Tree): 与线性映射相反,物理中断号比较大,或者不太连续时使用。
  • 直接映射(No Map/Diret): 有些中断控制器支持物理中断号编程,其被分配到的虚拟中断号可以被直接设定到中断控制器。
  • Legacy映射(传统映射): 特殊的一种映射模式。当需要固定分配一部分中断编号范围时使用。设备驱动可以直接使用约定的虚拟中断号来进行IRQ操作。

3.1.Linear

  The linear reverse map maintains a fixed size table indexed by the hwirq number. When a hwirq is mapped, an irq_desc is allocated for the hwirq, and the IRQ number is stored in the table.

  The Linear map is a good choice when the maximum number of hwirqs is fixed and a relatively small number (~ < 256).

  • The advantages of this map are fixed time lookup for IRQ numbers, and irq_descs are only allocated for in-use IRQs.
  • The disadvantage is that the table must be as large as the largest possible hwirq number.

  The majority of drivers should use the linear map.

  HW interrupt ID作为index,通过查表可以获取对应的IRQ number。对于Linear map而言,interrupt controller对其HW interrupt ID进行编码的时候要满足一定的条件:hw ID不能过大,而且ID排列最好是紧密的。对于线性映射,其接口API如下:

irq_domain_add_linear()
irq_domain_create_linear()

3.2.Tree

  The irq_domain maintains a radix tree map from hwirq numbers to Linux IRQs. When an hwirq is mapped, an irq_desc is allocated and the hwirq is used as the lookup key for the radix tree.

  The tree map is a good choice if the hwirq number can be very large since it doesn’t need to allocate a table as large as the largest hwirq number. The disadvantage is that hwirq to IRQ number lookup is dependent on how many entries are in the table.

  Very few drivers should need this mapping.

  建立一个Radix Tree来维护HW interrupt ID到IRQ number映射关系。HW interrupt ID作为lookup key,在Radix Tree检索到IRQ number。如果的确不能满足线性映射的条件,可以考虑Radix Tree map。对于Radix Tree map,其接口API如下:

irq_domain_add_tree()
irq_domain_create_tree()

3.3.No Map

  The No Map mapping is to be used when the hwirq number is programmable in the hardware. In this case it is best to program the Linux IRQ number into the hardware itself so that no mapping is required. Calling irq_create_direct_mapping() will allocate a Linux IRQ number and call the .map() callback so that driver can program the Linux IRQ number into the hardware.

  Most drivers cannot use this mapping.

  有些中断控制器很强,可以通过寄存器配置HW interrupt ID而不是由物理连接决定的。例如PowerPC 系统使用的MPIC (Multi-Processor Interrupt Controller)。在这种情况下,不需要进行映射,我们直接把IRQ number写入HW interrupt ID配置寄存器就OK了,这时候,生成的HW interrupt ID就是IRQ number,也就不需要进行mapping了。

irq_domain_add_nomap()

3.4.Legacy

  The Legacy mapping is a special case for drivers that already have a range of irq_descs allocated for the hwirqs. It is used when the driver cannot be immediately converted to use the linear mapping. For example, many embedded system board support files use a set of #defines for IRQ numbers that are passed to struct device registrations. In that case the Linux IRQ numbers cannot be dynamically assigned and the legacy mapping should be used.

  The legacy map assumes a contiguous range of IRQ numbers has already been allocated for the controller and that the IRQ number can be calculated by adding a fixed offset to the hwirq number, and visa-versa. The disadvantage is that it requires the interrupt controller to manage IRQ allocations and it requires an irq_desc to be allocated for every hwirq, even if it is unused.

  The legacy map should only be used if fixed IRQ mappings must be
supported. For example, ISA controllers would use the legacy map for
mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ
numbers.

  • irq_domain_add_simple()
  • irq_domain_add_legacy()
  • irq_domain_add_legacy_isa()

如下图所示:


  下图是针对3个控制器,结合nomap、linear、tree等3种映射方法,组成“irq_domain”的例子。

4.创建中断号映射

//创建HWIRQ映射,返回对应的虚拟中断号
extern unsigned int irq_create_mapping(struct irq_domain *host,irq_hw_number_t hwirq);//根据Firmware Spec创建映射,一般与设备树DTS解析的信息有关
extern unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec);//创建Direct Mapping
extern unsigned int irq_create_direct_mapping(struct irq_domain *host);//创建固定映射
extern int irq_create_strict_mappings(struct irq_domain *domain,unsigned int irq_base,irq_hw_number_t hwirq_base,int count);

4.1.irq_create_mapping

  驱动调用该函数的时候必须提供HW interrupt ID,也就是意味着driver知道自己使用的HW interrupt ID,而一般情况下,HW interrupt ID其实对具体的driver应该是不可见的,不过有些场景比较特殊,例如GPIO类型的中断,它的HW interrupt ID和GPIO有着特定的关系,driver知道自己使用哪个GPIO,也就是知道使用哪一个HW interrupt ID了。

4.2.irq_create_strict_mappings

  用来为一组HW interrupt ID建立映射。

4.3.irq_create_of_mapping

  利用device tree进行映射关系的建立。

4.4.irq_create_direct_mapping

  给no map那种类型interrupt controller使用。

5.Mapping 建立

5.1.概述

  系统中HW interrupt ID和IRQ number的mapping 是在整个系统初始化的过程中建立起来的,过程如下:

  • DTS文件描述了系统中的interrupt controller以及外设IRQ的拓扑结构,在linux kernel启动的时候,由bootloader传递给kernel(实际传递的是DTB);

  • 在Device Tree初始化的时候,形成了系统内所有的device node的树状结构,当然其中包括所有和中断拓扑相关的数据结构(所有的interrupt controller的node和使用中断的外设node);

  • 在machine driver初始化的时候会调用of_irq_init函数,在该函数中会扫描所有interrupt controller的节点,并调用适合的interrupt controller driver进行初始化。毫无疑问,初始化需要注意顺序,首先初始化root,然后first level,second level,最好是leaf node。在初始化的过程中,一般会调用上节中的接口函数向系统增加irq domain。有些interrupt controller会在其driver初始化的过程中创建映射;

  • 在各个driver初始化的过程中,创建映射。

5.2. device node转化为platform_device(重点分析irq)

相关代码:
drivers/of/platform.c

  当系统启动到board文件时,会调用.init_machine,各个平台定义该函数,接着调用of_platform_populate()接口,加载平台总线和平台设备。函数调用关系:

of_platform_default_populate_init---> of_platform_default_populate---> of_platform_populate---> of_platform_bus_create---> of_platform_device_create_pdata---> of_device_alloc---> of_irq_to_resource_table---> of_irq_to_resource--->of_irq_get---> of_irq_parse_one---> irq_create_of_mapping---> irq_create_fwspec_mapping---> irq_domain_translate //解析参数---> d->ops->translate (gic_irq_domain_translate)---> d->ops->xlate  ---> irq_domain_alloc_irqs---> gic_irq_domain_alloc  //执行软硬件的映射,并且根据中断类型设置struct irq_desc->handle_irq处理函数

实现如下:

 1 struct platform_device *of_device_alloc(struct device_node *np,2                   const char *bus_id,3                   struct device *parent)4 {5     struct platform_device *dev;6     int rc, i, num_reg = 0, num_irq;7     struct resource *res, temp_res;8     dev = platform_device_alloc("", -1);9     /* count the io and irq resources */
10   ...
11     num_irq = of_irq_count(np);       // 统计这个节点的interrupts属性中描述了几个中断
12     /* Populate the resource table */
13     if (num_irq || num_reg) {
14         res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
15    ...
16         dev->num_resources = num_reg + num_irq;
17         dev->resource = res;
18     ...
19     of_irq_to_resource_table(np, res, num_irq)  // 解析interrupts属性,将每一个中断转化为resource结构体
20     }
21     ...
22 }
  • of_irq_count

    这个函数会解析interrupts属性,并统计其中描述了几个中断。

  • of_irq_to_resource_table

    得知interrupts中描述了几个中断,开始将这些中断转换为resource。

  453 int of_irq_to_resource_table(struct device_node *dev, struct resource *res,                            454         int nr_irqs)455 {456     int i;457 458     for (i = 0; i < nr_irqs; i++, res++)459         if (of_irq_to_resource(dev, i, res) <= 0)460             break;461 462     return i;463 }
  • of_irq_to_resource:构建resource
 350 int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)351 {352     int irq = of_irq_get(dev, index);356 357     /* Only dereference the resource if both the358      * resource and the irq are valid. */359     if (r && irq) {360         const char *name = NULL;361 362         memset(r, 0, sizeof(*r));363         /*364          * Get optional "interrupt-names" property to add a name365          * to the resource.366          */367         of_property_read_string_index(dev, "interrupt-names", index,368                           &name);369 370         r->start = r->end = irq;371         r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));                      372         r->name = name ? name : of_node_full_name(dev);373     }374 375     return irq;376 }

  重点分析 of_irq_get,这个函数会获得device node,如pinctrl@11000000节点的interrupts属性的第index个中断的参数,这是通过of_irq_parse_one完成的,然后获得该中断所隶属的interrupt-controller的irq domain,利用该domain的of_xlate函数从前面表示第index个中断的参数中解析出hwirq和中断类型,最后从系统中为该hwriq分配一个全局唯一的virq,并将映射关系存放到中断控制器的irq domain中,也就是gic的irq domain。

  结合kernel代码分析:

of_irq_get
  ->of_irq_parse_one
  -> irq_create_of_mapping
    --> irq_create_fwspec_mapping
      —>gic_irq_domain_translate

of_irq_get:

388 int of_irq_get(struct device_node *dev, int index)389 {390     int rc;391     struct of_phandle_args oirq;392     struct irq_domain *domain;393 394     rc = of_irq_parse_one(dev, index, &oirq);  // 获得interrupts的第index个中断参数,并封装到oirq中398     domain = irq_find_host(oirq.np);                                                              401 402     return irq_create_of_mapping(&oirq);  //返回映射到的virq403 }

irq_create_fwspec_mapping:

 unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec){struct irq_domain *domain;struct irq_data *irq_data;irq_hw_number_t hwirq;unsigned int type = IRQ_TYPE_NONE;int virq;// 根据中断控制器的device_node找到所对应的irq domain,在前面GIC驱动注册irq domian的时候,// 会将irq_domain的fwnode设置为中断控制器的device_node的fwnode成员if (fwspec->fwnode) {domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);if (!domain)domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);} else {domain = irq_default_domain;}// 对于GIC的irq domain来说,会调用d->ops->translate(d, fwspec, hwirq, type)// 也就是gic_irq_domain_translate,对于没有定义translate的irq_domain,会调用d->ops->xlateirq_domain_translate(domain, fwspec, &hwirq, &type);...// 从这个irq domain查询看该hwirq之前是否已经映射过,一般情况下都没有virq = irq_find_mapping(domain, hwirq);if (virq) {...return virq;...}if (irq_domain_is_hierarchy(domain)) {  // 对于GIC的irq domain这样定义了alloc的domain来说,走这个分支virq = irq_domain_alloc_irqs(domain, , NUMA_NO_NODE, fwspec);} else {  // 其他没有定义irq_domain->ops->alloc的domain,走这个分支/* Create mapping */virq = irq_create_mapping(domain, hwirq);}irq_data = irq_get_irq_data(virq);/* Store trigger type */irqd_set_trigger_type(irq_data, type);return virq; //返回映射到的virq}

gic irq domain的translate过程:

 1104 static int gic_irq_domain_translate(struct irq_domain *d,1105                     struct irq_fwspec *fwspec,1106                     unsigned long *hwirq,1107                     unsigned int *type)1108 {   // 检查描述中断的参数个数是否合法1109     if (is_of_node(fwspec->fwnode)) {                                                                 1110         if (fwspec->param_count < 3)1111             return -EINVAL;1112 1113         switch (fwspec->param[0]) {1114         case 0:         /* SPI */1115             *hwirq = fwspec->param[1] + 32;1116             break;1117         case 1:         /* PPI */1118         case GIC_IRQ_TYPE_PARTITION:1119             *hwirq = fwspec->param[1] + 16;1120             break;1121         case GIC_IRQ_TYPE_LPI:  /* LPI */1122             *hwirq = fwspec->param[1];1123             break;1124         default:1125             return -EINVAL;1126         }1127 1128         *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;1130         /*1131          * Make it clear that broken DTs are... broken.1132          * Partitionned PPIs are an unfortunate exception.1133          */1134         WARN_ON(*type == IRQ_TYPE_NONE &&1135             fwspec->param[0] != GIC_IRQ_TYPE_PARTITION);1136         return 0;1137     }1138 1139     if (is_fwnode_irqchip(fwspec->fwnode)) {1140         if(fwspec->param_count != 2)1141             return -EINVAL;1142 1143         *hwirq = fwspec->param[0];1144         *type = fwspec->param[1];1145 1146         WARN_ON(*type == IRQ_TYPE_NONE);1147         return 0;1148     }                                                                                                 1149 1150     return -EINVAL;1151 }}

6.Debug

  CONFIG_IRQ_DOMAIN_DEBUG and CONFIG_GENERIC_IRQ_DEBUGFS is enabled. virq_debug_show and irq_debug_show functions show the debug information.

  • /sys/kernel/debug/irq_domain_mapping
  • /sys/kernel/irq
    • /sys/kernel/irq/irqs/
    • /sys/kernel/irq/domains
  • /proc/irq

6.1.代码

  • /kernel/irq/irqdomain.c
  • /kernel/irq/debugfs.c

refer to

  • https://l2h.site/2019/01/01/linux-interrupt-3/
  • https://www.cnblogs.com/pengdonglin137/p/6847685.html
  • https://blog.csdn.net/qq_33513098/article/details/88723282#s3c24xx_irq_map_of_441

linux IRQ Management(四)- IRQ Domain相关推荐

  1. linux IRQ Management(五)- irq_desc

    了解中断描述符struct irq_desc 参考韦东山百问网 1.通用中断代码处理   通用中断处理示意图:   对于每一个外设的IRQ 都用struct irq_desc来描述,称之为中断描述符. ...

  2. linux IRQ Management(三)- IRQ Framework

    了解irq management framework 1.Introduction   Linux is a system on which devices notify the kernel abo ...

  3. linux IRQ Management(六)- DTS及调试

    了解DTS Interrupt 设置方式. 1.DTS 中 interrupt 描述 interrupt-controller - 一个空的属性定义, 该节点作为一个接收中断信号的设备. #inter ...

  4. 深入学习Linux摄像头(四)三星平台fimc驱动详解

    深入学习Linux摄像头系列 深入学习Linux摄像头(一)v4l2应用编程 深入学习Linux摄像头(二)v4l2驱动框架 深入学习Linux摄像头(三)虚拟摄像头驱动分析 深入学习Linux摄像头 ...

  5. 内存管理:Linux Memory Management:MMU、段、分页、PAE、Cache、TLB

    目录 Linux Memory Management Memory Address Need for Virtual Addressing Address Translation Address Tr ...

  6. linux安装IPython四种方法

    IPython是Python的交互式Shell,提供了代码自动补完,自动缩进,高亮显示,执行Shell命令等非常有用的特性.特别是它的代码补完功能,例如:在输入zlib.之后按下Tab键,IPytho ...

  7. Linux入门第四集!Jar包的入门、使用、部署!怎么打Jar包?

    Linux入门第四集!Jar包的入门.使用.部署!怎么打Jar包? 一.首先要确保JDK8已经安装成功 Linux入门第三集!JDK8的Linux版本资源分享!jdk-8u301-linux-x64. ...

  8. Linux线程(四)

    Linux线程(四) 文章目录 Linux线程(四) 一.线程同步 二.生产者消费者模型 一.线程同步 1.条件变量 当一个线程互斥的访问某个变量时,他可能发现在其他线程改变状态之前,他自己什么都做不 ...

  9. Linux 串口编程四 串口设备程序开发

    Linux 串口编程和程序相对来说是很简单的,之所以用博客连载来展示,主要是想在学会使用的基础上掌握相关背景,原理以及注意事项.相信在遇到问题的时候,我们就不会对于技术的概念和 API 的使用浅尝辄止 ...

最新文章

  1. 在 Windows XP 中,无法使用 Windows 图片和传真查看器来查看图片
  2. Ardino基础教程 5_按键控制
  3. 机器学习/深度学习 问题总结及解答
  4. android安卓应用和OBD的集成场景
  5. 用Visual Studio 2019 开发stm32,cortex-m3, arm
  6. I - 滑雪 POJ - 1088(深搜,记忆化搜索)
  7. xml的方式配置AOP:Aspect Oriented Programming
  8. 想捧金饭碗? 修炼这25项技能就够了!
  9. pyCharm pyplot.show()不显示图表的解决
  10. php td复制剪贴板,选择一个带有Javascript的完整表格(复制到剪贴板)
  11. 5-1 File Transfer
  12. JAVA中使用Apache Batik实现SVG文件转PDF文件导出
  13. matlab解决线性规划问题
  14. 计算机网络编程基础知识总结思维导图
  15. 最简单的基金理财讲解
  16. #华为云#听从你心,无问西东
  17. Ubunu安装一个更新版本的gda(2.3.2到 3.0.4)
  18. 一分钟了解大数据的价值
  19. 一文看懂区块链技术安全,在安全行业区块链又有什么用
  20. CRC 循环冗余检验【计网必考】

热门文章

  1. 个人运营APP要投入多少钱?开发是小头,推广是大头!
  2. MySQL ERROR 1153 解决办法
  3. 数字冰雹 可视化html,数字冰雹数据可视化系列产品(二):网络态势可视化
  4. 天振股份上市首日破发:市值蒸发约8亿元,方庆华夫妇为实控人
  5. Unity Light 闪烁 最简单的实现方法
  6. echarts 刻度线
  7. 金蝶云星空单据转换设置
  8. 报销 交通栏 单据张数
  9. 面试技巧篇 -- 编码风格 (Coding Style)
  10. rosdep update失败问题解决