Linux Interrupt subsystem: the mapping and maintenance of interrupt numbers

Source: Internet
Author: User
Tags tidy

Write at the forefront:

Long time no calm down to tidy up some things, began to work for one months, the brain want to tidy up a lot of things. Record is a good way of self-learning, calm down more thinking and more summary, three years of work goals can not change, as a professional novice will enter the world Semiconductor first Intel working, is the opportunity is a challenge, difficult is conceivable. Down-to-earth, looking at the starry sky, results-oriented to the goal of the guidelines, strive to progress a little every day.


Linux kernel version: 3.4.39


I. IRQ_DESC initialization of the Linux interrupt subsystem

The initial interrupt initialization process for the Linux kernel is start_kernel. What the kernel space needs to do here is to initialize an interrupt descriptor table, which is done by the function early_irq_init, and the kernel can be configured to use a hash sheet or use linear directly. The number of kernel interrupts required for a general embedded platform is not very large, and all the ways to adopt Linera are more common, as follows:

struct Irq_desc Irq_desc[nr_irqs] __cacheline_aligned_in_smp = {[0 ... Nr_irqs-1] = {. handle_irq= handle_bad_irq,.depth= 1,.lock= __raw_spin_lock_unlocked (Irq_desc->lock),}};


Two. The GIC interrupt controller initialization process is common under the ARM platform:

The initialization of the GIC controller is mainly integrated under the arm platform, and the INIT_IRQ is responsible for the initialization of the GIC controllers at the lowest level, MACHINE_DESC->INIT_IRQ (); This process indicates that it is necessary to correlate the IRQ initialization associated with the machine platform, typically the first Init callback definition of the platform under the Arch/arcm/mach-xxx/board.x platform, such as Machine_init.

It is generally necessary to call the GIC initialized entry as Gic_of_init ():

int __init gic_of_init (struct device_node *node, struct device_node *parent) {void __iomem *cpu_base;void __iomem *dist_ba Se;u32 percpu_offset;int irq;if (warn_on (!node)) Return-enodev;dist_base = of_iomap (node, 0); WARN (!dist_base, "Unable to map GIC Dist registers\n"); cpu_base = Of_iomap (node, 1); WARN (!cpu_base, "Unable to map GIC CPU registers\n"), if (Of_property_read_u32 (node, "Cpu-offset", &percpu_offset)) Percpu_offset = 0;gic_init_bases (gic_cnt,-1, Dist_base, Cpu_base, Percpu_offset, node); if (parent) {IRQ = Irq_of_parse_a Nd_map (node, 0);//Add a child controller to the parent node with a normal interrupt, and map the GIC_CASCADE_IRQ (gic_cnt, IRQ);//Set the Cascade flow callback interface}//Cascade using this irq_num, The GIC needs to use itself as a sub-IRQ to map operations Gic_cnt++;return 0;

This process is mainly based on the Linux device tree, the current machine support Interrupt Controller init, the common processor with a single GIC, only some processors will appear root and child and other controllers cascade phenomenon. Assuming that a GIC is present, then gic_init_bases is the key to the initialization, and he mainly does the following:

Step1: Initialize gic_chip_data for maintenance of a GIC controller;

Step2: Calculates the interrupt number at the beginning of the current system hardware:

if (Gic_nr = = 0 && (Irq_start & +) > 0) {hwirq_base = 16;if (Irq_start! =-1) Irq_start = (Irq_start & ~31) + 16;} else {hwirq_base = 32;//HW IRQ starting from 32 i.e. SPI}

It should be explained here that the general arm platform GIC as a hardware interrupt controller, the specific interrupt number type has SGI software interruption, CPU private interrupt PPI, SPI, etc., the first two occupy 0-15,16-31 ID number, 32 start is the SPI interrupt type. While the general software interrupt is not necessary to maintain the corresponding interrupt descriptor, so if gic_nr=0 is initialized is the current root GIC, then the corresponding HWIRQ starting value is 16, including PPI, but PPI general and specific CPU binding privatization.


Step3: Adjusts the number of interrupts actually supported by the GIC used by the current platform.

Gic_irqs = readl_relaxed (Gic_data_dist_base (GIC) + gic_dist_ctr) & 0x1f;//gic Controller read support IRQ number Gic_irqs = (Gic_irqs + 1) * 32;if (Gic_irqs > 1020) Gic_irqs = 1020;gic->gic_irqs = Gic_irqs;//irqs Total Gic_irqs-= hwirq_base; /* Calculate # of IRQs to allocate *///actually minus 16 after the SPI to be mapped?


Setp4:irq_alloc_descs

Irq_base = Irq_alloc_descs (Irq_start, Gic_irqs, numa_node_id ());//Get an IRQ map table starting position

We know that the current use of a linear table to maintain the entire interrupt descriptor, the process is to find a Allocated_irqs map table in a non-0 start bit data location, and request a continuous number of GIC_IRQS in a map space, Indicates that this space is owned by the current root GIC, and the map bit value for this space is set to 1. For example, starting from 16 applications, 100 words, continuous cross-byte applications to 100bit.


Step5:irq_domain_add_legacy

Complete the establishment of a domain and complete the mapping between all hardware interrupt numbers HW_IRQ and VIR_IRQ.


Three. interrupt domain in the controller

The presence of interrupt controller domain is to better maintain the relationship between virtual interrupt number and hardware interrupt number, and to solve the IRQ mapping problem when multiple controller cascade is resolved, the Linux kernel provides a more standard mapping function interface. However, the GIC module is mapped and uses a simple processing method, as follows:

struct Irq_domain *irq_domain_add_legacy (struct device_node *of_node, unsigned int size, unsigned int FIRST_IRQ, irq_hw_n umber_t FIRST_HWIRQ, const struct irq_domain_ops *ops, void *host_data) {struct Irq_domain *domain;unsigned int i;domain = Irq_domain_alloc (Of_node, irq_domain_map_legacy, OPS, host_data); if (!domain) return NULL;DOMAIN-&GT;REVMAP_ DATA.LEGACY.FIRST_IRQ = FIRST_IRQ;DOMAIN-&GT;REVMAP_DATA.LEGACY.FIRST_HWIRQ = First_hwirq;domain->revmap_ Data.legacy.size = Size;mutex_lock (&irq_domain_mutex);/* Verify, all the IRQs is available */for (i = 0; I < s Ize i++) {int IRQ = FIRST_IRQ + i;struct irq_data *irq_data = irq_get_irq_data (IRQ); if (warn_on | |!irq_data In) {Mutex_unlock (&irq_domain_mutex); Of_node_put (Domain->of_node); kfree (domain); return NULL;}} /* Claim all of the IRQs before registering a legacy domain */for (i = 0; i < size; i++) {struct Irq_data *irq_data = i Rq_get_irq_data (FIRST_IRQ + i);//Gets the starting point of the VIRQ map table and assigns the value Hwirqirq_data-> HWIRQ = First_hwirq + I;irq_data->domain = domain;//binding Hwirq with Virq}mutex_unlock (&irq_domain_mutex); for (i = 0; I &l T Size i++) {int IRQ = FIRST_IRQ + I;int HWIRQ = First_hwirq + I;//offset Light is the same as/* IRQ0 gets ignored */if (!IRQ) continue;/* Legacy Flags is left-to-default at the this point, * One can then use Irq_create_mapping () to * explicitly change them */ops->map (Domain, IRQ, HWIRQ);//establish the relationship between IRQ and hardware interrupts/* Clear Norequest flags */irq_clear_status_flags (IRQ, irq_norequest);} Irq_domain_add (domain); return domain;}
Consider adding a domain processing process to the kernel, stating the key parameters passed in First_irq, FIRST_HWIRQ, which is the starting position of the Irq_desc available map bit space we applied to, the actual value is 16. The latter is the mapped interrupt number bit 16 supported by the current root GIC.


Step1: First, according to FIRST_IRQ to continuously obtain Irq_data essence is a irq_desc. The HWIRQ of this descriptor is then assigned to the domain to which it belongs. The benefit of this process is that it can be mapped to a unique VIRQ based on the matching hardware interrupt number. And it looks like a very linear mapping relationship.

struct Irq_data *irq_get_irq_data (unsigned int IRQ) {struct Irq_desc *desc = Irq_to_desc (IRQ); return desc? &desc-> Irq_data:null;}


Step2: Using the callback interface of domain Ops->map the initialization of the Flow_handler interface of the cutoff layer in the IRQ_DESC.

const struct Irq_domain_ops gic_irq_domain_ops = {. Map = Gic_irq_domain_map,.xlate = Gic_irq_domain_xlate,};

static int Gic_irq_domain_map (struct irq_domain *d, unsigned int irq,irq_hw_number_t hw) {if (HW < +) {Irq_set_percpu_d Evid (IRQ); Irq_set_chip_and_handler (IRQ, &gic_chip, HANDLE_PERCPU_DEVID_IRQ); Set_irq_flags (IRQ, IRQF_VALID | Irqf_noautoen);//ppi} else {Irq_set_chip_and_handler (IRQ, &gic_chip, HANDLE_FASTEOI_IRQ); Set_irq_flags (IRQ, Irqf_valid | Irqf_probe);//spi, all HANDLE_ARCH_IRQ type}irq_set_chip_data (IRQ, d->host_data); return 0;}
The SPI and PPI are handled separately by the Domain_map operation, and in the case of SPI, a callback is required primarily through Irq_set_chip_and_handler to set the current hardware interrupt to occur irq_decs->(irq_flow_handler_tHANDLE_IRQ;), this is not what we call the top-level Irq_handler for driver development, called a stream-layer callback in a Linux interrupt system, with the goal of handling different in-flow callbacks, such as multi-controller cascade, and PPI Flow_ Hander and so on.

Finally, the GIC chip level information is saved to this irq_desc.


Iv. initialization of GIC hardware modules:

Gic_dist_init (GIC);//This is the hardware Gic_distributor initialization gic_cpu_init (GIC),//CPU INTERFAC initialization gic_pm_init (GIC), initialization of the//gic power module


Summarize:

Domain is a good place to map a large number of hardware interrupts to a unique virtual interrupt number for the upper layer to drive the development of the REQUEST_IRQ, thus ensuring that the kernel does not appear irq_handler chaos in the event of an interrupt system call.





Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

Linux Interrupt subsystem: the mapping and maintenance of interrupt numbers

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.