Linux Kernel IRQ Domain

IRQ Domain – Interrupt number mapping library

The current design of the Linux kernel uses a single large number space where each separate IRQ source is assigned a different number. This is simple when there is only one interrupt controller, but in systems with multiple interrupt controllers the kernel must ensure that each one gets assigned non-overlapping allocations of Linux IRQ numbers.

The system in the past, there were only one interrupt controller, interrupt controller actual HW interrupt line number can become IRQ number. For example, earlier embedded SOC interrupt controller mostly had a single interrupt status register, the register might have 64 bit (probably more), each bit is an IRQ number, which can be directly mapped. At this time, GPIO interrupt status register in the interrupt controller is only one bit, so all GPIO interrupt only one IRQ number. So that the system has at least two interrupt controllers, one a traditional sense interrupt controller, one GPIO controller type interrupt controller.

With the increasing complexity of the system, the number of interrupt controllers registered as unique irqchips show a rising tendency: for example sub-drivers of different kinds such as GPIO controllers avoid re-implementing identical callback mechanisms as the IRQ core system by modelling their interrupt handlers as irqchips, i.e. in effect cascading interrupt controllers. For this reason we need a mechanism to separate controller-local interrupt numbers, called hardware irqs, from Linux IRQ numbers.

In the linux kernel, we use the following two ID to identify an interrupt from the peripheral:

  • Linux IRQ number: It is a kernel identifier used to talk about a hardware interrupt source. It is an enumeration of possible interrupt sources on a machine. Typically what is enumerated is the number of input pins on all of interrupt controller in the system. The IRQ number is a virtual interrupt ID, and hardware independent.
  • HW interrupt ID: It is a peripheral interrupt identifier in the actual hardware interrupt controller. Interrupt controller uses HW interrupt ID to identify the peripheral interrupts.

The irq_domain provides a mapping between hwirq and IRQ numbers. It also implements translation from Device Tree interrupt specifiers to hwirq numbers.

Types of Mapping

  • linear mapping: The linear mapping maintains a fixed size table indexed by the hwirq number. When a hwirq is mapped, an irq_desc is allocated for the hwirq, and the IRQ number is stored in the table. For a linear mapping, its interface API is as follows:
    static inline struct irq_domain * irq_domain_add_linear (struct device_node * of_node,
    unsigned int size,
    const struct irq_domain_ops * ops,
    void * host_data)
    irq_domain_add_linear() – Allocate and register a linear revmap irq_domain.
    @of_node: pointer to interrupt controller’s device tree node.
    @size: Number of interrupts in the domain.
    @ops: map/unmap domain callbacks
    @host_data: Controller private data pointer
  • Radix Tree map: The irq_domain maintains a radix tree map from hwirq numbers to Linux IRQs. When an hwirq is mapped, an irq_desc is allocated and the hwirq is used as the lookup key for the radix tree. For Radix Tree map, its interface API is as follows:
    static inline struct irq_domain * irq_domain_add_tree (struct device_node * of_node,
    const struct irq_domain_ops * ops,
    void * host_data)
  • No map: The No Map mapping is to be used when the hwirq number is programmable in the hardware. In this case it is configured via HW interrupt ID register rather than by the physical connection decisions. For this type of mapping, the interface API is as follows:
    static inline struct irq_domain * irq_domain_add_nomap (struct device_node * of_node,
    unsigned int max_irq,
    const struct irq_domain_ops * ops,
    void * host_data)
  • Legacy map: The Legacy mapping is a special case for drivers that already have a range of irq_descs allocated for the hwirqs. It is used when the driver cannot be immediately converted to use the linear mapping. For example, many embedded system board support files use a set of #defines for IRQ numbers that are passed to struct device registrations. In that case the Linux IRQ numbers cannot be dynamically assigned and the legacy mapping should be used. The legacy map assumes a contiguous range of IRQ numbers has already been allocated for the controller and that the IRQ number can be calculated by adding a fixed offset to the hwirq number, and visa-versa. For legacy map, its interface API is as follows:
    static inline struct irq_domain * irq_domain_add_legacy (struct device_node * of_node,
    unsigned int size,
    unsigned int first_irq,
    irq_hw_number_t first_hwirq,
    const struct irq_domain_ops * ops,
    void * host_data)
    irq_domain_add_legacy() – Allocate and register a legacy revmap irq_domain.
    @of_node: pointer to interrupt controller’s device tree node.
    @size: total number of irqs in legacy mapping
    @first_irq: first number of irq block assigned to the domain
    @first_hwirq: first hwirq number to use for the translation.
    @ops: map/unmap domain callbacks
    @host_data: Controller private data pointer
  • Hierarchy map: On some architectures, there may be multiple interrupt controllers involved in delivering an interrupt from the device to the target CPU.To support such a hardware topology and make software architecture match hardware architecture, an irq_domain data structure is built for each interrupt controller and those irq_domains are organized into hierarchy. When building irq_domain hierarchy, the irq_domain near to the device is child and the irq_domain near to CPU is parent. For legacy map, its interface API is as follows:
    static inline struct irq_domain * irq_domain_add_hierarchy (struct irq_domain *parent,
    unsigned int flags,
    unsigned int size,
    struct device_node *node,
    const struct irq_domain_ops * ops,
    void * host_data)
    irq_domain_add_hierarchy – Add a irqdomain into the hierarchy
    @parent: Parent irq domain to associate with the new domain
    @flags: Irq domain flags associated to the domain
    @size: Size of the domain. See below
    @node: Optional device-tree node of the interrupt controller
    @ops: Pointer to the interrupt domain callbacks
    @host_data: Controller private data pointer

Create a mapping for the IRQ domain

An interrupt controller driver creates and registers an irq_domain by calling one of the irq_domain_add_*() functions. The function will return a pointer to the irq_domain on success.

In most cases, the irq_domain will begin empty without any mappings between hwirq and IRQ numbers. Mappings are added to the irq_domain by calling irq_create_mapping() which accepts the irq_domain and a hwirq number as arguments. If a mapping for the hwirq doesn’t already exist then it will allocate a new Linux irq_desc, and calls the function irq_domain_associate to associate it with the hwirq, and call the .map() callback so the driver can perform any required hardware setup.

In case of a legacy mapping, where drivers already have a range of irq_descs allocated for the hwirqs and it assumes a contiguous range of IRQ numbers has already been allocated for the controller, so irq_domain_add_legacy calls the function irq_domain_associate_many, which in turn calls the function irq_domain_associate and associates the hwirq with linux irq number, and call the .map() callback so the driver can perform any required hardware setup.

Data Structure Descriptor

  • irq domain: in the kernel, the concept irq domain represented by struct irq_domain:
    struct irq_domain {
    struct list_head link;
    const char * name;
    const struct irq_domain_ops * ops;
    void * host_data;
    struct device_node * of_node;
    struct irq_domain_chip_generic * gc;
    #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
    struct irq_domain *parent;
    #endif
    irq_hw_number_t hwirq_max;
    unsigned int revmap_direct_max_irq;
    unsigned int revmap_size;
    struct radix_tree_root revmap_tree;
    unsigned int linear_revmap [];
    };
    struct irq_domain – Hardware interrupt number translation object
    @link: Element in global irq_domain list.
    @name: Name of interrupt domain
    @ops: pointer to irq_domain methods
    @host_data: private data pointer for use by owner. Not touched by irq_domain core code
    @flags: host per irq_domain flags
    @of_node: Pointer to device tree nodes, used to decode device tree interrupt specifiers
    @gc: Pointer to a list of generic chips
    @parent: Pointer to parent irq_domain to support hierarchy irq_domains
    @revmap_direct_max_irq: largest hwirq that can be set for controllers for direct mapping
    @revmap_size: Size of the linear map table @linear_revmap[]
    @revmap_tree: Radix map tree for hwirqs that don’t fit in the linear map
    @linear_revmap: Linear table of hwirq->virq reverse mappings

In linux kernel, all irq domain is linked into a global list, the list head is defined as follows:
static LIST_HEAD (irq_domain_list);
By irq_domain_list this pointer, you can get the entire system HW interrupt ID and IRQ number. The host_data define private data for the use of underlying interrupt controller.

  • irq domain callback interfaces: in the kernel irq domain callback function is abstracted by struct irq_domain_ops, defined as follows:
    struct irq_domain_ops {
    int (* match) (struct irq_domain * d, struct device_node * node);
    int (* map) (struct irq_domain * d, unsigned int virq, irq_hw_number_t hw);
    void (* unmap) (struct irq_domain * d, unsigned int virq);
    int (* xlate) (struct irq_domain * d, struct device_node * node,
    const u32 * intspec, unsigned int intsize,
    unsigned long * out_hwirq, unsigned int * out_type);
    };
    struct irq_domain_ops – Methods for irq_domain objects
    @match: Match an interrupt controller device node to a host, returns 1 on a match
    @map: Create or update a mapping between a virtual irq number and a hwirq number.
    @unmap: Dispose of such a mapping
    @xlate: Decode hwirq and linux irq from device tree node and interrupt specifier

The map callback function is implemented by the device driver and does the following operations:

  • Set the IRQ number corresponding interrupt descriptor (struct irq_desc) of irq chip
  • Set the IRQ number corresponding interrupt descriptor highlevel irq event handler
  • Sets the IRQ number corresponding interrupt descriptor irq chip data
This entry was posted in Technical and tagged , , , , , . Bookmark the permalink.

2 Responses to Linux Kernel IRQ Domain

  1. Pingback: Using gpio-generic and irq_chip_generic subsystems for gpio driver – Maquefel's Stash

  2. Pingback: Использование gpio-generic и irq_chip_generic для драйвера gpio — Maquefel's Stash

Leave a Reply

Your email address will not be published. Required fields are marked *