Interrupt handlers have a return value for a couple of reasons.

  1. Interrupt vectors can be shared between multiple devices. By returning IRQ_NONE/IRQ_HANDLED, an interrupt handler can indicate that the interrupt was/was not from the device it is specifically interested in. If IRQ_NONE is returned, the next handler in the list should be called.
  2. Even if the IRQ is not shared, an interrupt handler can indicate to the interrupt subsystem that there were problems handling the interrupt and that it should be disabled to prevent system hangs from an irq loop.
Answer from MikeK on Stack Overflow
🌐
Narkive
kernelnewbies.kernelnewbies.narkive.com › PXVsJA8X › irq-none-or-irq-handled
IRQ_NONE or IRQ_HANDLED
Post by Josh Cartwright Yes, if you have registered a handler on a shared IRQ, the first thing your handler must do is determine if it was your device which generated the interrupt. If it wasn't your device, you return IRQ_NONE.
🌐
Bootlin
elixir.bootlin.com › linux › v4.9 › ident › IRQ_NONE
IRQ_NONE identifier - Linux source code (v4.9) - Bootlin
include/linux/irqreturn.h, line 11 (as a enumerator) arch/arc/kernel/perf_event.c, line 428 · arch/arm/kernel/perf_event_v6.c, line 316 · arch/arm/kernel/perf_event_v7.c, line 966 · arch/arm/kernel/perf_event_xscale.c, 2 times · arch/arm/kernel/smp_twd.c, line 242 ·
🌐
Linux Kernel Labs
linux-kernel-labs.github.io › refs › heads › master › labs › interrupts.html
I/O access and Interrupts — The Linux Kernel documentation
The device driver must return IRQ_NONE if it notices that the interrupt has not been generated by the device it is in charge.
Top answer
1 of 3
15

This is covered in chapter 10 of Linux Device Drivers, 3rd edition, by Corbet et al. It is available for free online, or you may toss some shekels O'Reilly's way for dead tree or ebook forms. The part relevant to your question begins on page 278 in the first link.

For what it's worth, here is my attempt to paraphrase those three pages, plus other bits I've Googled up:

  • When you register a shared IRQ handler, the kernel checks that either:

    a. no other handler exists for that interrupt, or

    b. all of those previously registered also requested interrupt sharing

If either case applies, it then checks that your dev_id parameter is unique, so that the kernel can differentiate the multiple handlers, e.g. during handler removal.

  • When a PCI¹ hardware device raises the IRQ line, the kernel's low-level interrupt handler is called, and it in turn calls all of the registered interrupt handlers, passing each back the dev_id you used to register the handler via request_irq().

The dev_id value needs to be machine-unique. The common way to do that is to pass a pointer to the per-device struct your driver uses to manage that device. Since this pointer must be within your driver's memory space for it to be useful to the driver, it is ipso facto unique to that driver.²

If there are multiple drivers registered for a given interrupt, they will all be called when any of the devices raises that shared interrupt line. If it wasn't your driver's device that did this, your driver's interrupt handler needs to detect this and return IRQ_NONE promptly.

Another case is that your driver is managing multiple devices. The driver's interrupt handler should use dev_id to decide which device to poll.

The example Corbet et al. give is that of a PC parallel port. When it asserts the interrupt line, it also sets the top bit in its first device register. (That is, inb(0x378) & 0x80 == true, assuming standard I/O port numbering.) When your handler detects this, it is supposed to do its work, then clear the IRQ by writing the value read from the I/O port back to the port with the top bit cleared.

I don't see any reason that particular mechanism is special. A different hardware device could choose a different mechanism. The only important thing is that for a device to allow shared interrupts, it has to have some way for the driver to read the interrupt status of the device, and some way to clear the interrupt. You'll have to read your device's datasheet or programming manual to find out what mechanism your particular device uses.

  • When your interrupt handler tells the kernel it handled the interrupt, that doesn't stop the kernel from continuing to call any other handlers registered for that same interrupt. This is unavoidable if you are to share an interrupt line when using level-triggered interrupts.

Imagine two devices assert the same interrupt line at the same time. (Or at least, so close in time that the kernel doesn't have time to call an interrupt handler to clear the line and thereby see the second assertion as separate.) The kernel must call all handlers for that interrupt line, to give each a chance to query its associated hardware to see if it needs attention. It is quite possible for two different drivers to successfully handle an interrupt within the same pass through the handler list for a given interrupt.

Because of this, it is imperative that your driver tell the device it is managing to clear its interrupt assertion sometime before the interrupt handler returns. It's not clear to me what happens otherwise. The continuously-asserted interrupt line will either result in the kernel continuously calling the shared interrupt handlers, or it will mask the kernel's ability to see new interrupts so the handlers are never called. Either way, disaster.


Footnotes:

  1. I specified PCI above because all of the above assumes level-triggered interrupts, as used in the original PCI spec. ISA used edge-triggered interrupts, which made sharing tricky at best, and possible even then only when supported by the hardware. PCIe uses message-signalled interrupts; the interrupt message contains a unique value the kernel can use to avoid the round-robin guessing game required with PCI interrupt sharing. PCIe may eliminate the very need for interrupt sharing. (I don't know if it actually does, just that it has the potential to.)

  2. Linux kernel drivers all share the same memory space, but an unrelated driver isn't supposed to be mucking around in another's memory space. Unless you pass that pointer around, you can be pretty sure another driver isn't going to come up with that same value accidentally on its own.

2 of 3
5

When a driver requests a shared IRQ, it passes a pointer to the kernel to a reference to a device specific structure within the driver's memory space.

According to LDD3:

Whenever two or more drivers are sharing an interrupt line and the hardware interrupts the processor on that line, the kernel invokes every handler registered for that interrupt, passing each its own dev_id.

Upon checking several drivers' IRQ handlers, it appears they probe the hardware itself in order to determine whether or not it should handle the interrupt or return IRQ_NONE.

Examples

UHCI-HCD Driver
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

In the code above, the driver is reading the USBSTS register to determine if there is an interrupt to service.

SDHCI Driver
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

  if (!intmask || intmask == 0xffffffff) {
    result = IRQ_NONE;
    goto out;
  }

Just as in the previous example, the driver is checking a status register, SDHCI_INT_STATUS to determine whether it needs to service an interrupt.

Ath5k Driver
  struct ath5k_softc *sc = dev_id;
  struct ath5k_hw *ah = sc->ah;
  enum ath5k_int status;
  unsigned int counter = 1000;

  if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
        !ath5k_hw_is_intr_pending(ah)))
    return IRQ_NONE;

Just one more example.

🌐
LWN.net
lwn.net › Articles › 29555
device driver irq routine change in 2.5.x [LWN.net]
return IRQ_HANDLED; } instead. (the "IRQ_NONE" return is optional, but preferred: if the handler literally does nothing, and thinks that the interrupt was for somebody else, it should preferably return IRQ_NONE to tell the irq layer about it. HOWEVER - returning IRQ_NONE when you _should_ return ...
🌐
Bootlin
elixir.bootlin.com › linux › v2.6.28 › ident › IRQ_NONE
IRQ_NONE identifier - Linux source code (v2.6.28) - Bootlin
arch/mips/sni/irq.c, line 36 · arch/mips/sni/rm200.c, line 386 · arch/mips/txx9/generic/pci.c, line 256 · arch/parisc/kernel/smp.c, line 206 · arch/powerpc/platforms/83xx/suspend.c, line 126 · arch/powerpc/platforms/cell/spu_base.c, 2 times · arch/powerpc/platforms/powermac/pic.c, line 211 ·
🌐
Bootlin
elixir.bootlin.com › linux › latest › C › ident › IRQ_NONE
IRQ_NONE identifier - Linux source code (v6.7.6) - Bootlin
include/linux/irqreturn.h, line 12 (as a enumerator) arch/arc/kernel/perf_event.c, line 619 · arch/arm/kernel/perf_event_v6.c, line 315 · arch/arm/kernel/perf_event_v7.c, line 966 · arch/arm/kernel/perf_event_xscale.c, 2 times · arch/arm/kernel/smp_twd.c, line 189 ·
Find elsewhere
Top answer
1 of 2
7

The actual flags passed into request_irq() are defined in a comment in :

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
 * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
 * IRQF_SHARED - allow sharing the irq among several devices
 * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
 * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
 * IRQF_PERCPU - Interrupt is per cpu
 * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
 * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
 *                registered first in an shared interrupt is considered for
 *                performance reasons)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 */

These are bits, so a logical OR (ie |) of a subset of these can be passed in; and if none apply, then the empty set is perfectly fine -- ie a value 0 for the flags parameter.

Since IRQF_TRIGGER_NONE is 0, passing 0 into request_irq() just says leave the triggering configuration of the IRQ alone -- ie however the hardware/firmware configured it.

IRQ_NONE is in a different namespace; it is one of the possible return values of an interrupt handler (the function passed into request_irq()), and it means that the interrupt handler did not handle an interrupt.

2 of 2
0

IRQ_NONE is a constant for the return values of IRQ handlers. It is still available in include/linux/irqreturn.h.

IRQ_TRIGGER_NONE is a specifier for the behaviour of the interrupt line.

So they are not equivalent.

🌐
Linux Kernel
docs.kernel.org › core-api › genericirq.html
Linux generic IRQ handling — The Linux Kernel documentation
To make the transition to the new ... be used in more and more architectures, as it enables smaller and cleaner IRQ subsystems. It’s deprecated for three years now and about to be removed. None (knock on wood)....
🌐
Linux Kernel
lore.kernel.org › lkml › 1446016471.3405.201.camel@infradead.org
[PATCH] Document that IRQ_NONE should be returned ...
You are seeing this because the administrator of this website has set up Anubis to protect the server against the scourge of AI companies aggressively scraping websites. This can and does cause downtime for the websites, which makes their resources inaccessible for everyone · Anubis is a ...
🌐
Narkive
linux-arm-kernel.infradead.narkive.com › 8JLNRk6D › serial-8250-driver-causing-irq-nobody-cared-messages
serial 8250 driver causing "irq nobody cared" messages
This appears to be because a second UART interrupt occurs while the serial8250_interrupt is running, this interrupt is then delayed by the logic in do_edge_IRQ. The first invocation of the interrupt handler deals with this second interrupt before returning, at which point the delayed interrupt is dispatched. However there is now no work to be done so serial8250_interrupt returns IRQ_NONE.
🌐
Google Groups
groups.google.com › g › fa.linux.kernel › c › JlhF_o9Qygc
[PATCH] gpio: mpc8xxx: don't set IRQ_TYPE_NONE when creating irq mapping
Thomas renamed the old set_irq_type() call to irq_set_irq_type(), but this line setting the IRQ_TYPE_NONE type was there before renaming. It was added by commit 345e5c8a. At this time set_irq_type() checked its type argument and returned 0 if the type argument didn't specify some meaningful type in its type sense bits (and thus was equal to IRQ_TYPE_NONE).
🌐
GitHub
github.com › OP-TEE › optee_os › blob › master › core › include › kernel › interrupt.h
optee_os/core/include/kernel/interrupt.h at master · OP-TEE/optee_os
* or IRQ_TYPE_NONE if not. Can be NULL if not needed · * @prio interrupt priority if specified by interrupt property or 0 if not. Can · * be NULL if not needed · * * Returns the interrupt number if value >= 0 · * otherwise DT_INFO_INVALID_INTERRUPT ·
Author   OP-TEE
🌐
LWN.net
lwn.net › Articles › 787809
Support wakeup capable GPIOs [LWN.net]
May 7, 2019 - Wakeup capabable: GPIO ---> PDC ------> GIC Requesting a GPIO as an interrupt through the gpio_to_irq() call would setup an interrupt hierarchy as above and return the linux interrupt number. However, since the trigger type of the GPIO is unknown at this time, gpiolib defaults to IRQ_TYPE_NONE.
🌐
Rabexc
sbexr.rabexc.org › latest › sources › 8e › 4b9949ec9d9c70.html
Linux v6.6.1 - include/linux/irq.h - rabexc.org
* * Bits 0-7 are the same as the IRQF_* bits in linux/interrupt.h * * IRQ_TYPE_NONE - default, unspecified type * IRQ_TYPE_EDGE_RISING - rising edge triggered * IRQ_TYPE_EDGE_FALLING - falling edge triggered * IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered * IRQ_TYPE_LEVEL_HIGH - high ...
🌐
The Linux Documentation Project
tldp.org › HOWTO › Plug-and-Play-HOWTO-10.html
Plug-and-Play-HOWTO: Interrupt Sharing and Interrupt Conflicts
One case when an ISA device is activated but can't be assigned an interrupt (IRQ) since none are available.