irq_domain 分析

linux 中断简述中,对整个中断框架做了一个简单的概述。其中irq_domain在中断中的位置,也做了概述。记下来分析irq_domain的具体使用。

1.irq doamin创建申请

申请一个irq doamin,可使用的接口如下,这些接口调用的核心接口为 __irq_domain_add。

struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
					 unsigned int size, unsigned int first_irq,
					 const struct irq_domain_ops *ops, void *host_data);
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);
static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
					 unsigned int size, const struct irq_domain_ops *ops,
					 void *host_data);
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);
static inline struct irq_domain *irq_domain_add_legacy_isa(
				struct device_node *of_node, const struct irq_domain_ops *ops,
				void *host_data);
static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
					 const struct irq_domain_ops *ops, void *host_data);
static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *fwnode,
					 unsigned int size, const struct irq_domain_ops *ops,
					 void *host_data);
static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
					 const struct irq_domain_ops *ops, void *host_data);
extern struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
			unsigned int flags, unsigned int size, struct fwnode_handle *fwnode,
			const struct irq_domain_ops *ops, void *host_data);

static inline struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
					    unsigned int flags, unsigned int size, struct device_node *node,
					    const struct irq_domain_ops *ops, void *host_data);

1.1 irq_domain_add_simple & irq_domain_add_legacy,这两个接口实现irq domain的创建,且可以绑定用户已经申请的irq desc。不同的是,irq_domain_add_simple如果入参first_irq为0,则不会执行绑定操作,irq_domain_add_legacy则默认irq_base被配置为正确的irq desc。first_irq为用户已经申请过的virq rangs的初始编号。例:

	irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
	if (irq_base < 0) {
		err = irq_base;
		goto err_out;
	}

	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, &irq_domain_simple_ops, NULL);
	if (!port->domain) {
		err = -ENODEV;
		goto err_out;
	}

 1.2 irq_domain_add_hierarchy&irq_domain_create_hierarchy,这两个接口实现的是在父irq domain节点下继续创建一个irq domain。如GPIO中断可以该接口,将GPIO控制器的irq domain挂在GIC中断下。

	parent_np = of_irq_find_parent(dev->of_node);
	if (!parent_np)
		return -ENXIO;

	parent_domain = irq_find_host(parent_np);
	of_node_put(parent_np);
	if (!parent_domain)
		return -EPROBE_DEFER;

	priv->domain = irq_domain_create_hierarchy(
					parent_domain, 0, XXX_GPIO_IRQ_MAX_NUM,
					of_node_to_fwnode(dev->of_node), &xxx_gpio_irq_domain_ops, priv);

1.3 __irq_domain_add 申明如下,其中比较关键的几个参数为size, hwirq_max,direct_max。

size: 如果非0的话,则存储hwirq和virq的映射关系的空间将为线性的,关系映射存储地址为unsigned int linear_revmap[ ]。反之则为链式存储,存储地址为struct radix_tree_root revmap_tree。

hwirq_max:定义了此irq_domain的最大子中断号。该值限定在domain域下申请子中断时,最大的子中断号,如果超过该值,则产生一个WARN。

direct_max:该值仅用于不映射的情况下,hwirq即为virq的情况。如果子中断为hwirq的话,direct_max被配置为0。

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);

通过以上__irq_domain_add分析,便可以理解如下几个封装好的接口。

irq_domain_add_linear、irq_domain_create_linear:Virq与Hwirq的映射关系存储为线性区域的domain创建。其实现如下:

static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
					 unsigned int size,
					 const struct irq_domain_ops *ops,
					 void *host_data)
{
	return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
}

irq_domain_add_tree、irq_domain_create_tree:与上面的接口不一样的是, 该接口的实现的映射关系存储为基数树radix_tree方式(基数树相关算法不详述),其映射关系以数的关系建立,可实现映射关系动态插入和删除,一般用于中断数目较多,且应用到的数目较少的情况,如GIC控制器实现了500个中断,但通常应用场景只使用了80个中断的情况。其实现如下:

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_to_fwnode(of_node), 0, ~0, 0, ops, host_data);
}

irq_domain_add_nomap: 不建立virq和hwirq的映射关系,hwirq即为virq。direct_max为max_irq,其实现如下:

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_to_fwnode(of_node), 0, max_irq, max_irq, ops, host_data);
}

irq_domain_add_legacy_isa:部分传统的ISA控制器只能使用预留的virq 0~15作为中断,所以内核提供相关的该接口,方便ISA控制器注册中断号0~15,NUM_ISA_INTERRUPTS值为16。其实现如下:

static inline struct irq_domain *irq_domain_add_legacy_isa(
				struct device_node *of_node,
				const struct irq_domain_ops *ops,
				void *host_data)
{
	return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
				     host_data);
}

2. 常用的Virq中断申请

irq_create_mapping:根据中断所属的irq_domain,直接map出来Virq。该接口的实现为先查询中断是否已经map,如果没有map则alloc一个irq desc,然后将desc的virq在domain中建立映射关系。

of_irq_get:使用dts的配置申请irq,首先找到interrput-parent的节点,如果domain中没有map这个中断号,则会调用irq_create_mapping接口去申请一个Virq,并绑定在父domain中,建立其map关系。

platform_get_irq:当device为一个platform device时,可通过该接口申请一个virq。该接口底层根据device的配置去申请中断。如果配置dts,则调用of_irq_get接口申请;如果创建device时,预先配置Virq到resource中的IORESOURCE_IRQ类型,则将resource中的值读出来返回;另外如果配置了acpi方式,也会使用acpi_irq_get等方式去申请Virq(此处不描述)。

gpio_to_irq:顾名思义将根据GPIO号获取一个virq。需要GPIO控制驱动端实现gpio_chip的to_irq的功能,有些驱动做在pinctrl端,有些做在gpio控制器端。

pci_irq_vector: 获取pci dev下的Virq,通常为msi或msi-X中断,pci dev的中断Virq会在dev枚举时,linux驱动框架提前map好(过程请参考pcie相关流程),此接口即返回对应的Virq。

pci_request_irq:不关心Virq,直接注册对应pci dev的中断服务,其核心是request_threaded_irq,其中Virq的参数使用pci_irq_vector来获取。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的蜗牛87

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值