Linux内核中IRQ Domain的结构、操作及映射机制详解

往期内容

本专栏往期内容,interrtupr子系统:

  1. 深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    – 末片,有专栏内容观看顺序

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有专栏内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看顺序

img

1.数据结构描述

1.1 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 nodestruct 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 IDunsigned int revmap_direct_max_irq; ----unsigned int revmap_size; ---线性映射的size,for Radix Tree map和no map,该值等于0struct radix_tree_root revmap_tree; ----Radix Tree map使用到的radix tree root nodeunsigned 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数据结构)。

对于线性映射:

(1)linear_revmap保存了一个线性的lookup table,index是HW interrupt ID,table中保存了IRQ number值

(2)revmap_size等于线性的lookup table的size。

(3)hwirq_max保存了最大的HW interrupt ID

(4)revmap_direct_max_irq没有用,设定为0。revmap_tree没有用。

对于Radix Tree map:

(1)linear_revmap没有用,revmap_size等于0。

(2)hwirq_max没有用,设定为一个最大值。

(3)revmap_direct_max_irq没有用,设定为0。

(4)revmap_tree指向Radix tree的root node。

1.2 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);
#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);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);
#endif
};}

xlate:大概就是将设备树中节点的hwirq and type解析出来。就是将指定的设备(node参数)上若干个(intsize参数)中断属性(intspec参数)翻译成HW interrupt ID(out_hwirq参数)和trigger类型(out_type)。

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

map:调用map函数的时机是在创建(或者更新)HW interrupt ID(hw参数)和IRQ number(virq参数)关系的时候。貌似还有的根据hirq找到对应的virq,应该是在irq_set_chip_and_handler调用时提供的handle函数中

从发生一个中断到调用该中断的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

就是根据传入的virq,设置对应的irq_desc中的irq_chip、handle函数、irq_data:irq_set_chip_data()、irq_set_chip_and_handler()

alloc:如果是gic_irq_domain_alloc函数的话

不仅负责分配虚拟 IRQ 号并建立硬件中断号(hwirq)和虚拟 IRQ 号(virq)之间的映射,还会为分配的 IRQ 号设置相应的 irq_descirq_data 结构体。这包括配置中断处理程序 (handle_irq)、中断类型、触发模式等与中断相关的详细信息。

当调用 gic_irq_domain_alloc 时,它通常会执行以下任务:

  1. 分配 IRQ 描述符 (**irq_desc**):

    • 为特定的虚拟 IRQ 号分配一个 irq_desc 结构体。这是中断子系统用来管理特定中断的信息块,包含状态、处理程序等。
  2. 设置中断处理程序 (**handle_irq**):

    • irq_desc 结构体中的 handle_irq 字段是一个函数指针,用于指定当中断发生时应该调用哪个处理函数。在 GIC 的情况下,这可能是一个标准的 GIC 处理程序,例如 handle_fasteoi_irq
  3. 配置 **irq_data** 结构:

    • irq_datairq_desc 的一部分,包含与中断相关的硬件信息。gic_irq_domain_alloc 会填充这个结构,包括:

      • hwirq(硬件中断号)。
      • 中断控制器的地址、数据和特定于硬件的配置。
  4. 设置中断触发类型和属性:

    • 可能会设置中断的触发模式(边沿触发或电平触发)以及其他属性,如中断优先级和屏蔽状态。
  5. 建立映射关系:

    • irq_descirq_data 与特定的 hwirqvirq 关联起来,这样当中断发生时,内核可以正确地调用相应的处理程序。

translate: 用于将硬件中断号(hwirq)从设备树或其他硬件描述符中翻译成域内部的表示。

2.irq_domain的用法

中断控制器驱动程序通过以下方式创建并注册一个irq_domain。

irq_domain_add_*()

irq_domain_create_*()

每个映射方法都有不 同的分配器函数(比如irq_domain_add_linear内部调用的是irq_domain_add_)

函数成功后会返回一个指向irq_domain的指针。 调用者必须向分配器函数提供一个irq_domain_ops结构体。

在大多数情况下,irq_domain在开始时是空的,没有任何hwirq和IRQ号之间的映射。 通过调用irq_create_mapping()将映射添加到irq_domain中,该函数接受 irq_domain和一个hwirq号作为参数。 如果hwirq的映射还不存在,那么它将分配 一个新的Linux irq_desc,将其与hwirq关联起来,并调用.map()回调,这样驱动 程序就可以执行任何必要的硬件设置。

一旦建立了映射,可以通过多种方法检索或使用它:

  • irq_resolve_mapping()返回一个指向给定域和hwirq号的irq_desc结构指针, 如果没有映射则返回NULL。
  • irq_find_mapping()返回给定域和hwirq的Linux IRQ号,如果没有映射则返回0。
  • irq_linear_revmap()现与irq_find_mapping()相同,已被废弃。
  • generic_handle_domain_irq()处理一个由域和hwirq号描述的中断。

请注意,irq域的查找必须发生在与RCU读临界区兼容的上下文中。

在调用irq_find_mapping()之前,至少要调用一次irq_create_mapping()函数, 以免描述符不能被分配。

如果驱动程序有Linux的IRQ号或irq_data指针,并且需要知道相关的hwirq号(比 如在irq_chip回调中),那么可以直接从irq_data->hwirq中获得。

3.irq_domain映射

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

(1)DTS文件描述了系统中的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 level,second level,最好是leaf node。在初始化的过程中,一般会调用上节中的接口函数向系统增加irq domain。有些interrupt controller会在其driver初始化的过程中创建映射

(4)在各个driver初始化的过程中,创建映射

当一个hwirq被映射 时,会给hwirq分配一个irq_desc,并将irq号存储在表中:hwirq和irq之间的映射

img

3.1 注册映射

3.1.1 线性映射

其实就是一个lookup table,HW interrupt ID作为index,通过查表可以获取对应的IRQ number。对于Linear map而言,interrupt controller对其HW interrupt ID进行编码的时候要满足一定的条件:hw ID不能过大,而且ID排列最好是紧密的。

static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node,unsigned int size,---------该interrupt domain支持多少IRQconst struct irq_domain_ops *ops,---callback函数void *host_data)-----driver私有数据
{return __irq_domain_add(of_node, size, size, 0, ops, host_data);
}------------------------------------------------------------------
irq_domain_create_linear()

线性反向映射维护了一个固定大小的表,该表以hwirq号为索引。 当一个hwirq被映射 时,会给hwirq分配一个irq_desc,并将irq号存储在表中。

当最大的hwirq号固定且数量相对较少时,线性图是一个很好的选择(~<256)。 这种 映射的优点是固定时间查找IRQ号,而且irq_descs只分配给在用的IRQ。 缺点是该表 必须尽可能大的hwirq号。

irq_domain_add_linear()在功能上是等价的, 除了第一个参数不同–前者接受一个Open Firmware特定的 ‘struct device_node’ 而 后者接受一个更通用的抽象 ‘struct fwnode_handle’ 。

大多数驱动应该使用线性映射

3.1.2 树状映射

建立一个Radix Tree来维护HW interrupt ID到IRQ number映射关系。HW interrupt ID作为lookup key,在Radix Tree检索到IRQ number。如果的确不能满足线性映射的条件,可以考虑Radix Tree map。实际上,内核中使用Radix Tree map的只有powerPC和MIPS的硬件平台。

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);
}------------------------------------------------------------------
irq_domain_create_tree()

irq_domain维护着从hwirq号到Linux IRQ的radix的树状映射。 当一个hwirq被映射时, 一个irq_desc被分配,hwirq被用作radix树的查找键。

如果hwirq号可以非常大,树状映射是一个很好的选择,因为它不需要分配一个和最大hwirq 号一样大的表。 缺点是,hwirq到IRQ号的查找取决于表中有多少条目。

irq_domain_add_tree()和irq_domain_create_tree()在功能上是等价的,除了第一 个参数不同——前者接受一个Open Firmware特定的 ‘struct device_node’ ,而后者接受 一个更通用的抽象 ‘struct fwnode_handle’ 。

很少有驱动应该需要这个映射。

3.1.3 无映射

些中断控制器很强,可以通过寄存器配置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()

当硬件中的hwirq号是可编程的时候,就可以采用无映射类型。 在这种情况下,最好将 Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create_direct_mapping() 会分配一个Linux IRQ号,并调用.map()回调,这样驱动就可以将Linux IRQ号编入硬件中。

大多数驱动程序无法使用此映射,现在它由CONFIG_IRQ_DOMAIN_NOMAP选项控制。 这类接口的逻辑很简单,根据自己的映射类型,初始化struct irq_domain中的各个成员,调用__irq_domain_add将该irq domain挂入irq_domain_list的全局列表。

3.1.4 传统映射类型

irq_domain_add_simple()
irq_domain_add_legacy()
irq_domain_create_simple()
irq_domain_create_legacy()

传统映射是已经为 hwirqs 分配了一系列 irq_descs 的驱动程序的特殊情况。 当驱动程 序不能立即转换为使用线性映射时,就会使用它。 例如,许多嵌入式系统板卡支持文件使用 一组用于IRQ号的定义(#define),这些定义被传递给struct设备注册。 在这种情况下, 不能动态分配Linux IRQ号,应该使用传统映射。

顾名思义,_legacy()系列函数已被废弃,只是为了方便对古老平台的支持而存在。 不应该增加新的用户。当_simple()系列函数的使用导致遗留行为时,他们也是如此。

传统映射假设已经为控制器分配了一个连续的IRQ号范围,并且可以通过向hwirq号添加一 个固定的偏移来计算IRQ号,反之亦然。 缺点是需要中断控制器管理IRQ分配,并且需要为每 个hwirq分配一个irq_desc,即使它没有被使用。

只有在必须支持固定的IRQ映射时,才应使用传统映射。 例如,ISA控制器将使用传统映射来 映射Linux IRQ 0-15,这样现有的ISA驱动程序就能得到正确的IRQ号。

大多数使用传统映射的用户应该使用irq_domain_add_simple()或 irq_domain_create_simple(),只有在系统提供IRQ范围时才会使用传统域,否则将使用 线性域映射。这个调用的语义是这样的:如果指定了一个IRQ范围,那么 描述符将被即时分配 给它,如果没有范围被分配,它将不会执行 irq_domain_add_linear() 或 irq_domain_create_linear(),这意味着 no irq 描述符将被分配。

一个简单域的典型用例是,irqchip供应商同时支持动态和静态IRQ分配。

为了避免最终出现使用线性域而没有描述符被分配的情况,确保使用简单域的驱动程序在任何 irq_find_mapping()之前调用irq_create_mapping()是非常重要的,因为后者实际上 将用于静态IRQ分配情况。

irq_domain_add_simple()和irq_domain_create_simple()以及 irq_domain_add_legacy()和irq_domain_create_legacy()在功能上是等价的,只 是第一个参数不同–前者接受Open Firmware特定的 ‘struct device_node’ ,而后者 接受一个更通用的抽象 ‘struct fwnode_handle’ 。

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 domainfirst_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 0~15对应的IRQ无效16号IRQ  <------------------>16号HW interrupt ID17号IRQ  <------------------>17号HW interrupt ID……

3.1.5 IRQ域层级结构

在某些架构上,可能有多个中断控制器参与将一个中断从设备传送到目标CPU。 x86平台上典型的中断传递路径:

Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU

涉及到的中断控制器有三个:

  1. IOAPIC 控制器
  2. 中断重映射控制器
  3. Local APIC 控制器

为了支持这样的硬件拓扑结构,使软件架构与硬件架构相匹配,为每个中断控制器建立一 个irq_domain数据结构,并将这些irq_domain组织成层次结构。

在建立irq_domain层次结构时,靠近设备的irq_domain为子域,靠近CPU的 irq_domain为父域。所以在上面的例子中,将建立如下的层次结构。

CPU Vector irq_domain (root irq_domain to manage CPU vectors)^|
Interrupt Remapping irq_domain (manage irq_remapping entries)^|
IOAPIC irq_domain (manage IOAPIC delivery entries/pins)

使用irq_domain层次结构的主要接口有四个:

  1. irq_domain_alloc_irqs(): 分配IRQ描述符和与中断控制器相关的资源来传递这些中断。
  2. irq_domain_free_irqs(): 释放IRQ描述符和与这些中断相关的中断控制器资源。
  3. irq_domain_activate_irq(): 激活中断控制器硬件以传递中断。
  4. irq_domain_deactivate_irq(): 停用中断控制器硬件,停止传递中断。

为了支持irq_domain层次结构,需要做如下修改:

  1. 一个新的字段 ‘parent’ 被添加到irq_domain结构中;它用于维护irq_domain的层次信息。
  2. 一个新的字段 ‘parent_data’ 被添加到irq_data结构中;它用于建立层次结构irq_data以 匹配irq_domain层次结构。irq_data用于存储irq_domain指针和硬件irq号。
  3. 新的回调被添加到irq_domain_ops结构中,以支持层次结构的irq_domain操作。

在支持分层irq_domain和分层irq_data准备就绪后,为每个中断控制器建立一个irq_domain结 构,并为每个与IRQ相关联的irq_domain分配一个irq_data结构。现在我们可以再进一步支持堆 栈式(层次结构)的irq_chip。也就是说,一个irq_chip与层次结构中的每个irq_data相关联。 一个子irq_chip可以自己或通过与它的父irq_chip合作来实现一个所需的操作。

通过堆栈式的irq_chip,中断控制器驱动只需要处理自己管理的硬件,在需要的时候可以向其父 irq_chip请求服务。所以我们可以实现更简洁的软件架构。

为了让中断控制器驱动程序支持irq_domain层次结构,它需要做到以下几点:

  1. 实现 irq_domain_ops.alloc 和 irq_domain_ops.free
  2. 可选择地实现 irq_domain_ops.activate 和 irq_domain_ops.deactivate.
  3. 可选择地实现一个irq_chip来管理中断控制器硬件。
  4. 不需要实现irq_domain_ops.map和irq_domain_ops.unmap,它们在层次结构 irq_domain中是不用的。

irq_domain层次结构绝不是x86特有的,大量用于支持其他架构,如ARM、ARM64等。

3.2 创建映射

上面是注册了一个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了。

(2)irq_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);                   

(3)irq_create_of_mapping。看到函数名字中的of(open 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。

(4)irq_create_direct_mapping。这是给no map那种类型的interrupt controller使用的

3.3 中断号映射库

目前Linux内核的设计使用了一个巨大的数字空间,每个独立的IRQ源都被分配了一个不 同的数字。 当只有一个中断控制器时,这很简单,但在有多个中断控制器的系统中,内核必须确保每 个中断控制器都能得到非重复的Linux IRQ号(数字)分配。

注册为唯一的irqchips的中断控制器编号呈现出上升的趋势:例如GPIO控制器等不同 种类的子驱动程序通过将其中断处理程序建模为irqchips,即实际上是级联中断控制器, 避免了重新实现与IRQ核心系统相同的回调机制。

在这里,中断号与硬件中断号离散了所有种类的对应关系:而在过去,IRQ号可以选择, 使它们与硬件IRQ线进入根中断控制器(即实际向CPU发射中断线的组件)相匹配,现 在这个编号仅仅是一个数字。

出于这个原因,我们需要一种机制将控制器本地中断号(即硬件irq编号)与Linux IRQ 号分开。

irq_alloc_desc*() 和 irq_free_desc*() API 提供了对irq号的分配,但它们不 提供任何对控制器本地IRQ(hwirq)号到Linux IRQ号空间的反向映射的支持。

irq_domain 库在 irq_alloc_desc*() API 的基础上增加了 hwirq 和 IRQ 号码 之间的映射。 相比于中断控制器驱动开放编码自己的反向映射方案,更喜欢用 irq_domain来管理映射。

irq_domain还实现了从抽象的irq_fwspec结构体到hwirq号的转换(到目前为止是 Device Tree和ACPI GSI),并且可以很容易地扩展以支持其它IRQ拓扑数据源。

这也使得用户在调用irq_alloc_desc后,hwirq和irq之间已经有了映射,再通过调用3.1中注册irq_domain的接口(比如irq_domain_add_legacy)获得已经有映射关系的irq_domain,不需要再调用irq_create_mapping函数建立HW interrupt ID和IRQ number的映射关系。

irq_alloc_desc*() 系列函数:
这些函数用于分配一系列的 IRQ 描述符,并返回一个起始的 IRQ 号码(irq_base)。当你使用这些 API 时,可以选择是否指定 hwirq 和 IRQ 号码之间的映射。
这些函数可以直接通过参数来设定映射关系,例如在分配描述符时提供的 hwirq 和 irq_base 值。
在调用这些 API 时,内核会自动处理并维护 hwirq 和 IRQ 号码之间的映射关系,这在某种程度上相当于调用了 irq_create_mapping()。irq_create_mapping() 函数:
该函数是更直接的用于在现有的 irq_domain 中创建或查询硬件中断号(hwirq)和虚拟 IRQ 号之间的映射。
它将指定的 hwirq 映射到 irq_domain 中的一个对应的 IRQ 号码,并返回这个 IRQ 号码。
如果映射已经存在,它会直接返回现有的映射关系中的 IRQ 号码。
共同点和区别:
共同点:两者都可以建立硬件中断号(hwirq)与虚拟 IRQ 号(virq)之间的映射,并在系统中维护这一关系。
区别:
irq_alloc_desc*() 不仅仅是用于映射,还涉及到分配 IRQ 描述符的工作。它为新分配的 IRQ 提供了内存空间并初始化描述符。
irq_create_mapping() 更专注于映射本身,通常在描述符已经分配好的情况下使用它来建立或查找 hwirq 和 virq 之间的映射。

3.5 其它情况

static struct irq_chip virtual_intc_irq_chip = {.name			= "100ask_virtual_intc",.irq_ack	   = virtual_intc_irq_ack	   ,.irq_mask	   = virtual_intc_irq_mask	   ,.irq_mask_ack  = virtual_intc_irq_mask_ack ,.irq_unmask    = virtual_intc_irq_unmask   ,.irq_eoi	   = virtual_intc_irq_eoi	   ,
};static int virtual_intc_irq_map(struct irq_domain *h, unsigned int virq,irq_hw_number_t hw)
{/* 1. 给virq提供处理函数* 2. 提供irq_chip用来mask/unmask中断*/irq_set_chip_data(virq, h->host_data);//irq_set_chip_and_handler(virq, &virtual_intc_irq_chip, handle_edge_irq); /* handle_edge_irq就是handleC */irq_set_chip_and_handler(virq, &virtual_intc_irq_chip, handle_level_irq); /* handle_level_irq就是handleC *///irq_set_nested_thread(virq, 1);//irq_set_noprobe(virq);return 0;
}static const struct irq_domain_ops virtual_intc_domain_ops = {.xlate = irq_domain_xlate_onetwocell,.map   = virtual_intc_irq_map,
};static int virtual_intc_probe(struct platform_device *pdev)
{	struct device_node *np = pdev->dev.of_node;int irq_to_parent;//int irq_base;/* 1. virutal intc 会向GIC发出n号中断 *//* 1.1 从设备树里获得virq_n */irq_to_parent = platform_get_irq(pdev, 0);printk("virtual_intc_probe irq_to_parent = %d\n", irq_to_parent);/* 1.2 设置它的irq_desc[].handle_irq, 它的功能时分辨是哪一个hwirq, 调用对应的irq_desc[].handle_irq */irq_set_chained_handler_and_data(irq_to_parent, virtual_intc_irq_handler, NULL);/* 2. 分配/设置/注册一个irq_domain *///irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id());//printk("virtual_intc_probe irq_base = %d\n", irq_base);/* Usage:*  a. dts: 定义使用哪个hwirq*  b. 内核解析设备树时分配irq_desc,得到virq*  c. (hwirq, virq) ==>存入domain*/virtual_intc_domain = irq_domain_add_linear(np, 4, &virtual_intc_domain_ops, NULL);return 0;
}

在很多情况下,使用 irq_domain_add_linear 创建 IRQ 域后,确实需要调用 irq_create_mapping 或者 irq_domain_map 来建立硬件中断号(hwirq)和虚拟中断号(virq)之间的映射。

然而,在某些情况下,尤其是像你代码中描述的情形,如果设备树(DTS)已经定义了中断资源,并且系统在解析设备树的时候自动分配了中断描述符(irq_desc),则可能不需要显式调用 irq_create_mapping。具体原因如下:

\1. 设备树自动映射:

  • 当设备树中已经定义了硬件中断号(hwirq)时,设备树解析器(OF)可能会自动为这些 hwirq 分配相应的虚拟中断号(virq),并将其存储在 irq_domain 中。
  • 这种情况下,映射可能是在设备树解析过程中由框架自动完成的,因此不需要手动调用 irq_create_mapping(上面代码应该就是处于这种情况,或者也有可能是irq_alloc_descs调用时已经完成了irq和hwirq之间的映射)

\2. 特定中断控制器驱动的逻辑:

  • 某些中断控制器驱动在调用 irq_domain_add_linear 时,会直接处理硬件中断号(hwirq)的映射,可能在 irq_domain_ops 回调函数中自动处理 hwirq 和 virq 的关联。
  • 使用的 irq_domain_ops(如 virtual_intc_domain_ops)可能已经内置了自动映射的逻辑,因此省去了显式调用 irq_create_mapping 的步骤。

\3. 使用平台的现有机制:

  • 如果基于某个平台或者已有的中断控制器框架,那么它们可能已经实现了自动分配和映射的机制。只要你按规定创建 irq_domain,框架可能会处理其余的细节。

虽然大多数情况下需要调用 irq_create_mapping 来显式建立 hwirq 和 virq 的映射,但在某些特定环境下,尤其是当设备树和平台驱动框架已经处理了这些映射时,你可以不需要显式调用这个函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9036.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

cocos creator 3.8.3物理组件分组的坑

坑&#xff0c;坑的不行的大坑 group用的二进制的左移获取十进制的数值 目前是这样判断的&#xff0c;也不知道对不对&#xff0c;什么get、set Group没找到

如何解决“在ANACONDA prompt可以使用‘conda activate‘,但是在pycharm终端没办法使用该指令“”

一、设置好环境变量 此电脑&#xff08;右键&#xff09;-属性-高级系统设置-环境变量-在系统变量那一筐的PATH双击添加 二、完成后再测试conda activate 笔者测试完后&#xff0c;conda是可以用了&#xff0c;但是conda activate用不了。 显示该错误CommandNotFoundError:…

三菱MR-J4-B系列伺服参数一览

要点 与伺服系统控制器连接后&#xff0c;同服系统控制器的伺服参数的值即被写入各参数中。根据伺服系统控制器的机种和伺服放大器软件版本及MRConfigurator2的软件版本&#xff0c;存在无法设定的参数或范围。详细内容请参照伺服系统控制器的用户手册。请使用MR Configurator2…

Pattern program MPAT 详解

本文为VIP文章,主要介绍Pattern中元素与格式、常用指令、地址&数据产生指令等。 目录 一、pattern概述 二:Pattern构成元素 1、pattern构成元素:MPAT、END 2、pattern构成元素:pattern file name 3、pattern构成元素:SDEF 4、Pattern构成元素:REGISETR 5、Pa…

Qt编译lua库并调用

参考博客&#xff1a; 编译lua库 参考下面文章编译lua库文件 QT5.9学习笔记之QT编译lua库_qtluaintf.h-CSDN博客 https://blog.csdn.net/qq_23345187/article/details/112710677 Qt代码引用lua库文件 打开pro项目文件&#xff0c;右键空白处&#xff0c;点击添加库&#xff…

数据结构:直接插入排序

直接插入排序是数据结构中较为简单的插入排序法&#xff0c;基本思想是&#xff1a;把待排序的记录按照关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插完为止&#xff0c;得到一个新的有序序列。 实际上我们玩扑克时&#xff0c;就用了插入排…

Pr 视频过渡:溶解

效果面板/视频过渡/溶解 Video Transitions/Dissolve Adobe Premiere Pro 的视频过渡效果中&#xff0c;溶解 Dissolve效果组提供了多种平滑过渡方式&#xff0c;适用于不同的场景需求&#xff0c;从基础的淡入淡出到复杂的叠加溶解&#xff0c;帮助用户实现更具层次感的视觉过…

使用 Elasticsearch 构建食谱搜索(一)

作者&#xff1a;来自 Elastic Andre Luiz 了解如何使用 Elasticsearch 构建基于语义搜索的食谱搜索。 简介 许多电子商务网站都希望增强其食谱搜索体验。正确使用语义搜索可以让客户根据更自然的查询&#xff08;例如 “something for Valentines Day - 情人节的礼物” 或 “…

prompt资料收集

1. LANGgpt模板 # Role: 知识探索专家 ## Profile: - - 即刻App即刻App&#xff0c;享受探索、表达和创造https://m.okjike.com/originalPosts/649801f1ba47fe581a0da471?seyJ1IjoiNjQyM2IwMDE4NDg5Njk1NGJjYzhkNWU1IiwiZCI6MX0%3D2. 好的prompt的标准 主观的说&#xff1a;…

大数据学习10之Hive高级

1.Hive高级 将大的文件按照某一列属性进行GROUP BY 就是分区&#xff0c;只是默认开窗存储&#xff1b; 分区是按行&#xff0c;如一百行数据&#xff0c;按十位上的数字分区&#xff0c;则有十个分区&#xff0c;每个分区里有十行&#xff1b; 分桶是根据某个字段哈希对桶数取…

前端Nginx的安装与应用

目录 一、前端跨域方式 1.1、CORS(跨域资源共享) 1.2、JSONP(已过时) 1.3、WebSocket 1.4、PostMessage 1.5、Nginx 二、安装 三、应用 四、命令 4.1、基本操作命令 4.2、nginx.conf介绍 4.2.1、location模块 4.2.2、反向代理配置 4.2.3、负载均衡模块 4.2.4、通…

Mit6.S081-实验环境搭建

Mit6.S081-实验环境搭建 注&#xff1a;大家每次做一些操作的时候觉得不太保险就先把虚拟机克隆一份 前言 qemu&#xff08;quick emulator&#xff09;&#xff1a;这是一个模拟硬件环境的软件&#xff0c;利用它可以运行我们编译好的操作系统。 准备一个Linux系统&#xf…

AWS账号安全:如何防范与应对账号被盗风险

在云计算时代&#xff0c;Amazon Web Services&#xff08;AWS&#xff09;作为全球领先的云服务提供商&#xff0c;为企业和个人提供了强大的计算资源和灵活的服务。然而&#xff0c;随着云计算的普及&#xff0c;AWS账号被盗的风险也随之增加。我们九河云有多年用云经验&…

IPTABLE:Linux下的网络防火墙

IPTABLE&#xff1a;Linux下的网络防火墙 引言 在Linux系统中&#xff0c;IPtable是一种强大的网络防火墙工具&#xff0c;广泛应用于各种网络环境中。它不仅可以实现基本的包过滤功能&#xff0c;还能进行网络地址转换&#xff08;NAT&#xff09;、数据包记录、流量统计等高…

Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…

友思特应用 | 动态捕捉:高光谱相机用于移动产线上的食品检测

导读 高光谱成像技术能够为食品安全助力。以友思特BlackIndustry SWIR 1.7 Max 为代表的高光谱相机&#xff0c;完美解决了移动产线检测的应用难点。 高光谱技术&#xff1a;为食品安全保驾护航 食品安全一直是大众关心的热点话题&#xff0c;提供安全、高质量的食品需要对食…

Java——》try-with-resource

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

【极客兔兔-Web框架Gee详解】Day0 序言

文章目录 一、Web 开发1. 什么是Web 开发&#xff1f;2. 主要组成部分2.1 前端开发2.2 后端开发2.3 全栈开发2.4 数据库管理 3. Web开发过程3.1 规划和设计&#xff1a;3.2 开发和编码&#xff1a;3.3 测试和优化&#xff1a;3.4 部署和维护&#xff1a; 4. 总结 二、用标准库n…

点击文本将内容填入tinymce-vue 富文本编辑器的光标处

富文本编辑器组件 <template><div ref"tinymceBox" class"tinymce-box"><Editor id"myEditor" v-model"contentValue" :init"init" :disabled"disabled" blur"inputBlur" click"o…

3.2cpu

这个转换原理是基于&#xff0c;地址号*大小页内偏移量&#xff0c;通过页表使逻辑号和内存号之间建立起联系&#xff0c;从而实现地址的转换 按字节寻址&#xff0c;意思是说一个地址的大小是一个字节 页表记录的是逻辑块号与实际存储的主存块号之间的映射关系&#xff0c;是…