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