Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Open Source Software and Platforms
Open Source Software and Platforms
Wiki Arm Interrupt handling for GIC controller acpi
  • Help
  • Jump...
  • Cancel
  • About this wiki
  • Supported platforms
  • Obtaining support
  • +Arm Reference Platforms deliverables
  • +A-class platforms
  • +M-class platforms
  • +R-class platforms
  • +FPGA prototyping boards
  • +Open source software

You are currently reviewing an older revision of this page.

  • History View current version

Arm Interrupt handling for GIC controller acpi

The generic interrupt handling layer

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.

irq domain

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.

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

Step By Step

    • ACPI bus scan the acpi device (ace for short) based on the ACPI table in the firmware.
    • Get the Interrupt resource of ace from the _CRS field in the ACPI table. Below is an ACPI device _CRS example in ACPI DSDT:
                   Device(COM0) {    
                             Name(_HID, "ARMH0011")    
                             Name(_CID, "PL011")    
                             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.

    Kernel also allocate a struct irq_desc instance desc and insert the pair {{7-->95}, desc} into a radix tree:

              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.

    Above steps happen in kernel boot phase. Now comes to the run time for the interrupt handler:

    • Interrupt handler read ICC_IAR1 and get the value 95
    • Kernel lookup the radix tree and find the corresponding irq = 7
    • Get the desc by using irq = 7
    • handle the interrupt

    Some tips

    The exchange method between hwirq and 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);

    Reference

    [1] Documentation/DocBook/genericirq