This article describes the most important concepts related to the Linux kernel's interrupt handling mechanisms. These concepts include the relevant code and data structures. Sample code from Linux kernel version 2.6.12 is also provided.
struct irqdesc and do_IRQ
Each interrupt source available to the system has allocated to it a single struct irqdesc structure. This structure stores important information for the interrupt controller, handler and others:
The handle field points to a high-level "handler" for the interrupt line asserting the interrupt request. In ARM architectures, the handler is called do_level_IRQ:void do_level_IRQ (unsigned int irq,
Do_level_IRQ acknowledges the interrupt request, then invokes the list of device interrupt handlers registered with that interrupt source by calling __do_irq. We will come back to this function in a moment.
The chip field refers to the functions that manage the interrupt controller hardware. It is not uncommon for hardware that supports Linux to contain several different interrupt controllers, each having its own procedure for enabling, disabling and acknowledging interrupts. Most PCs have two interrupt controllers; some microcontrollers used in embedded applications have one or two built-in controllers, with one or more "external" interrupt controllers implemented using programmable logic devices.
The struct irqchip structure looks like this:
The action field of struct irqdesc maintains a list of struct irqaction structures, each of which represents a device interrupt handler that has registered with the interrupt request line using request_irq. The __do_irq function "walks" this list each time the interrupt request line is serviced:
The pend field is an ARM-specific field, used to make sure that pending interrupt requests are fully serviced during interrupt handling.
The chipdata field is another ARM-specific field, used by interrupt controller handlers as a private data pointer. Some ARM implementations like SA1111 use this field to store the physical address of the interrupt controller that manages the request line. Others, like AT91RM920, use this pointer to store the physical address of the GPIO controller that manages the line.
The data field is another ARM-specific field, used by device interrupt handlers as a private data pointer. Many device drivers uses this field to store a pointer to per device data structures.
The disable_depth field keeps interrupt enable and disable requests balanced. An interrupt request line is not truly disabled until the number of disable requests matches the number of enable requests.
Handling an interrupt request
When the host microcontroller responds to an interrupt request, control first goes to a bit of assembly language code that knows how to store register values and other information critical to restoring the machine state after the interrupt is serviced. In ARM machines, the function is called __irq_svc:
With the current processor state saved away, ARM machines then invoke asm_do_IRQ:
The desc->handle() invocation calls do_level_IRQ or do_edge_IRQ, depending on the type of interrupt request line being serviced.
A basic interrupt handler
The Cogent Computer Systems, Inc. CSB637 single board computer has a pushbutton connected to GPIO PB29. When pressed, this pushbutton sends an edge-triggered interrupt to the GPIO controller, which forwards the request to the interrupt controller.
(A bit of magic further demultiplexes the interrupt request to a unique irqdesc. This magic is necessary because the AT91RM9200's GPIO controllers each have only one line leading to the chip's interrupt controller. This magic is well hidden from device interrupt handlers.)
A simple "device interrupt handler" for this pushbutton might look like the following:
This code would be registered with the Linux kernel's request_irq function:
Probe_irq_on and probe_irq_off
The Linux kernel's interrupt management system can help you determine which physical interrupt request line is assigned to your device. This feature is also useful for confirming that the interrupt request line is actually functioning before your system commits to using it.
The following code shows how to "probe" for an interrupt request line:
"Virtual" interrupt descriptors
A multifunction chip might use one interrupt request line to signal interrupt requests from all of its onboard functions. Many multichannel UART chips have only a single interrupt request line leading back to the host microcontroller, for example.
Device drivers for multifunction chips can often be made more reusable and flexible if they can focus on only one feature of the target chip. The SM501 graphics processor has built-in AC97, UART and USBH, but only one interrupt request line back to the host processor.
Rather than writing a combined video-plus-audio-plus-USB that would only be useful for that chip, with some effort it is possible to re-use the Linux kernel's standard framebuffer, audio and USB device drivers instead.
To do so, you must "demultiplex" the interrupt request line for the multifunction chip so that the signaling sub-component can be serviced by the right driver. Under Linux, this is done by creating "virtual interrupt descriptors" that look like unique interrupt sources for the purposes of device drivers:
The device interrupt handler for the physical interrupt request line gets the interrupt request first. It then reads the interrupt status from the chip, and redirects the interrupt request to the descriptor associated with the function requesting service:
A clear understanding of the Linux kernel's interrupt handling mechanism is essential if you are to write solid, reusable device interrupt handlers. It is also mandatory if you are to successfully port Linux to custom hardware.
Bill Gatliff is a freelance embedded developer and training consultant with 10 years of experience using GNU and other tools for building embedded systems targeting automotive, aerospace, and medical instrumentation applications. He is a contributing editor for Embedded Systems Design, author of the Embedded GNU Jumpstart and Embedded Linux Jumpstart series of training materials. He con be contacted at firstname.lastname@example.org.
This article is excerpted from a paper of the same name presented
at the Embedded Systems Conference Boston 2006. Used with permission of
the Embedded Systems Conference. For more information, please visit www.embedded.com/esc/boston/.