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


此博文基于对苯叔中断系统视频的学习以及对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;
}
  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Linux系统中,硬中断是由硬件设备生成和分配的。当硬件设备需要处理某个事件时,会向CPU发送一个中断,表示需要CPU的处理。这个中断会被操作系统捕获,并被内核处理。 在Linux系统中,硬中断是通过中断控制器(Interrupt Controller)来产生的。中断控制器是一个硬件设备,用于管理和分配中断请求。常见的中断控制器有APIC(Advanced Programmable Interrupt Controller)、IO APIC和PCI-MSI(Peripheral Component Interconnect-Message Signaled Interrupts)。 当硬件设备需要处理某个事件并向CPU发出中断时,中断控制器会接收到该中断。然后,中断控制器会将中断转发给CPU,并为该中断分配一个硬中断。硬中断是一个唯一的数字标识符,用于区分不同的中断。 内核会将硬中断保存在一个中断描述符表(Interrupt Descriptor Table)中。该表记录了每个硬件设备的硬中断和对应的中断处理程序地址。当有硬件中断发生时,CPU会根据硬中断中断描述符表中查找对应的中断处理程序,并跳转到该程序执行相应的处理逻辑。 总结来说,Linux系统中的硬中断是由中断控制器产生和分配的。硬件设备向CPU发送中断后,中断控制器会为该中断分配一个硬中断,并将其保存在中断描述符表中,以便CPU能够正确地调用相应的中断处理程序。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值