Maybe we all know the classic __do_IRQ() in the kernel, but as the so called a 'generic interrupt handling layer' introduced, the __do_IRQ will be deprecated over the next two-or-three years, more and more new architecture will use the new generic interrupt handling layer instead. But both of these two frameworks will co-exist for the time being. Arm64 interrupt handling will use the new interrupt handling framework while not the older one.
__do_IRQ()
__do_IRQ
Data struct irq_domain is used in Linux kernel as an object with some actions attached like hardware interrupt number translation, etc, which will be bound to each of the arch-specific interrupt controller instance created.
irq_domain
In Arm world, GICv3 ACPI driver will insert the irq_domain instance domain into a global link list variable irq_domain_list.
The ops of this domain for Arm GIC interrupt controller as below:
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, };
The term 'translate' here maybe confused, but I don't want to give more details about that, codes will disclose everything:)
Device(COM0) { Name(_HID, "ARMH0011") Name(_UID, Zero) Name(_CRS, ResourceTemplate() { Memory32Fixed(ReadWrite, 0x2a400000, 0x1000) Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 95 } }) }
So the ACPI core will get 95 from the above _CRS settings, 95 is the hardware interrupt number or hwirq or GSI(Global System Interrupt) number, they have the same meaning in this post otherwise specified. So the ACPI generator must know the hardware connection with interrupt controller to decide the interrupt line number, it's 95 in this example. Let me clarify some terms here again as emphasize, 95 here is hwirq from kernel point, it's INTID from ARM GICV3/4 point, but remember they are the same thing. The key point here is, when kernel read the GIC ICC_IAR1 register, it will get 95, while not other value. This is very important to make the interrupt handling works.
Kernel maps this hwirq 95 to an irq which is used by kernel (likewise, irq is the same thing as IRQ number in kernel source code) as an index to lookup its corresponding interrupt handler, say maybe irq = 7 from the kernel perspective. acpi_register_gsi() is used to do that:
acpi_register_gsi() | |--> irq_create_fwspec_mapping();
irq_create_fwspec_mapping() is used to map the GSI to the IRQ number. In order to do that, it will lookup in a radix tree first to see if current GSI has already been mapped to an IRQ number, if not, allocate and fill it in the tree by calling irq_domain_alloc_irqs(), the latter will use a bitmap as the container to track which IRQ number has been allocated, at the same time it will allocate a struct irq_desc object, and insert the pair {irq, desc} into the radix tree 'irq_desc_tree'. After that, kernel will insert the pair {hwirq, struct irq_data object} into domain>revmap_tree which is also a radix tree.
irq_create_fwspec_mapping()
irq_domain_alloc_irqs()
irq_desc object
{irq, desc}
'irq_desc_tree'
{hwirq, struct irq_data object}
domain>revmap_tree
Kernel also allocate a struct irq_desc instance desc and insert the pair {{7-->95}, desc} into a radix tree:
irq_desc
{{7-->95}, desc}
kernel/irq/irqdomain.c: __irq_domain_alloc_irqs(); | |--> irq_domain_insert_irq(virq + …);
Back to the ACPI parse topic. If we use the res as the ACPI interrupt resource of that device, then we can simple record it as res->start = irq after above steps. So ACPI interrupt resource only record the IRQ number while not the hwirq for the kernel.
res->start = irq
Above steps happen in kernel boot phase. Now comes to the run time for the interrupt handler:
The exchange method between hwirq and irq:
hwirq
irq:
struct irq_data *data = irq_get_irq_data(irq); hwirq = data->hwirq;
The exchange method between irq and desc:
desc = irq_to_desc(irq);
[1] Documentation/DocBook/genericirq