linux硬件中断(hwirq)和软件中断号(virq)的映射过程

本文详细介绍了Linux内核中硬件中断号(hw_irq)如何通过中断域转换为虚拟中断号(virq)的过程,涉及设备树解析、中断类型的判断以及中断映射的具体步骤。重点讲解了gic_irq_domain_translate函数在不同中断类型下的hw_irq计算,并阐述了hw_irq和virq映射的实现,包括irq_create_fwspec_mapping函数的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


此博文基于对苯叔中断系统视频的学习以及对linux内核源码的理解总结而成

1 硬件中断号获取

在具体的硬件初始化过程,通过会获取到对应的hw_irq,然后转换为virq,最后用于注册对应的中断。
硬件中断号一般是通过解析设备树或者直接用板级数据获取,获取之后通过irq_of_parse_and_map函数转换为virq,然后再去注册中断。

1.1 硬件中断号的获取过程

|- irq_of_parse_and_map
	|- of_irq_parse_one
	|- irq_create_of_mapping
		|- irq_create_fwspec_mapping
			|- irq_domain_translate
				|- d->ops->translate
					|- gic_irq_domain_translate
					
static const struct irq_domain_ops gic_irq_domain_ops = {
	.translate = gic_irq_domain_translate,
	.alloc = gic_irq_domain_alloc,
	.free = gic_irq_domain_free,
	.select = gic_irq_domain_select,
};

1.2 gic_irq_domain_translate

在设备树中,对于中断的描述以k3-am65.dtsi为例来说明,示例如下所示:

 52  a53_timer0: timer-cl0-cpu0 {
 53          compatible = "arm,armv8-timer";
 54          interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>, /* cntpsirq */
 55                       <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>, /* cntpnsirq */
 56                       <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>, /* cntvirq */
 57                       <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>; /* cnthpirq */
 58  };

interrupts域描述了中断的相关属性,分别使用3个属性来描述一个中断,这三个参数是在of_irq_parse_one函数中解析出来的。

  • 1 中断类型,对应于设备树中的GIC_PPI属性,表示是PPI中断,0表示SPI中断。
  • 2 中断ID,对应于设备树中的13、14、11、10这几个中断号,表示的是hw_irq号。
  • 3 中断触发类型,对应于IRQ_TYPE_LEVEL_LOW,表示低电平触发中断,中断触发类型还包括高电平触发,边沿触发等类型。

如何获取硬件终端号:

  • 判断中断类型fwspec->param[0]
    • SPI中断*hwirq = fwspec->param[1] + 32;
    • PPI中断*hwirq = fwspec->param[1] + 16;
    • EPPI中断*hwirq = fwspec->param[1] + ESPI_BASE_INTID;
    • LPI中断*hwirq = fwspec->param[1];
static int gic_irq_domain_translate(struct irq_domain *d,
				    struct irq_fwspec *fwspec,
				    unsigned long *hwirq,
				    unsigned int *type)
{
	if (is_of_node(fwspec->fwnode)) {
		if (fwspec->param_count < 3)
			return -EINVAL;

		switch (fwspec->param[0]) {
		case 0:			/* SPI */
			*hwirq = fwspec->param[1] + 32;
			break;
		case 1:			/* PPI */
			*hwirq = fwspec->param[1] + 16;
			break;
		case 2:			/* ESPI */
			*hwirq = fwspec->param[1] + ESPI_BASE_INTID;
			break;
		case 3:			/* EPPI */
			*hwirq = fwspec->param[1] + EPPI_BASE_INTID;
			break;
		case GIC_IRQ_TYPE_LPI:	/* LPI */
			*hwirq = fwspec->param[1];
			break;
		case GIC_IRQ_TYPE_PARTITION:
			*hwirq = fwspec->param[1];
			if (fwspec->param[1] >= 16)
				*hwirq += EPPI_BASE_INTID - 16;
			else
				*hwirq += 16;
			break;
		default:
			return -EINVAL;
		}

		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;

		/*
		 * Make it clear that broken DTs are... broken.
		 * Partitionned PPIs are an unfortunate exception.
		 */
		WARN_ON(*type == IRQ_TYPE_NONE &&
			fwspec->param[0] != GIC_IRQ_TYPE_PARTITION);
		return 0;
	}

	if (is_fwnode_irqchip(fwspec->fwnode)) {
		if(fwspec->param_count != 2)
			return -EINVAL;

		*hwirq = fwspec->param[0];
		*type = fwspec->param[1];

		WARN_ON(*type == IRQ_TYPE_NONE);
		return 0;
	}

	return -EINVAL;
}

2 hw_irq和virq映射过程

2.1 hw_irq和virq映射的过程

|- irq_of_parse_and_map
	|- of_irq_parse_one
	|- irq_create_of_mapping
		|- irq_create_fwspec_mapping
			|- virq = irq_find_mapping(domain, hwirq);
			|- virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec)
				|- __irq_domain_alloc_irqs
					|- irq_domain_alloc_descs
						|- __irq_alloc_descs
							|- start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS, from, cnt, 0);
							|- ret = alloc_descs(start, cnt, node, affinity, owner)
			|- irq_data = irq_get_irq_data(virq);
			|- irqd_set_trigger_type(irq_data, type);

2.2 irq_create_fwspec_mapping

irq_create_fwspec_mapping函数主要功能是由下面五个函数组成的

  • irq_domain_translate(domain, fwspec, &hwirq, &type),获取硬件中断号
  • virq = irq_find_mapping(domain, hwirq);,查看hw_irq是否已经被映射过
  • virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec),申请virq,该函数用于hw_irq和virq的映射处理
  • irq_data = irq_get_irq_data(virq); 根据virq获取irq_data
  • irqd_set_trigger_type(irq_data, type);,注册中断触发类型
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;

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

	if (!domain) {
		pr_warn("no irq domain found for %s !\n",
			of_node_full_name(to_of_node(fwspec->fwnode)));
		return 0;
	}

	if (irq_domain_translate(domain, fwspec, &hwirq, &type))
		return 0;

	/*
	 * WARN if the irqchip returns a type with bits
	 * outside the sense mask set and clear these bits.
	 */
	if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
		type &= IRQ_TYPE_SENSE_MASK;

	/*
	 * If we've already configured this interrupt,
	 * don't do it again, or hell will break loose.
	 */
	virq = irq_find_mapping(domain, hwirq);
	if (virq) {
		/*
		 * If the trigger type is not specified or matches the
		 * current trigger type then we are done so return the
		 * interrupt number.
		 */
		if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
			return virq;

		/*
		 * If the trigger type has not been set yet, then set
		 * it now and return the interrupt number.
		 */
		if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
			irq_data = irq_get_irq_data(virq);
			if (!irq_data)
				return 0;

			irqd_set_trigger_type(irq_data, type);
			return virq;
		}

		pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
			hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
		return 0;
	}

	if (irq_domain_is_hierarchy(domain)) {
		virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
		if (virq <= 0)
			return 0;
	} else {
		/* Create mapping */
		virq = irq_create_mapping(domain, hwirq);
		if (!virq)
			return virq;
	}

	irq_data = irq_get_irq_data(virq);
	if (!irq_data) {
		if (irq_domain_is_hierarchy(domain))
			irq_domain_free_irqs(virq, 1);
		else
			irq_dispose_mapping(virq);
		return 0;
	}

	/* Store trigger type */
	irqd_set_trigger_type(irq_data, type);

	return virq;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值