mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 20:32:22 +00:00
670310dfba
Pull irq core updates from Thomas Gleixner: "A rather large update for the interrupt core code and the irq chip drivers: - Add a new bitmap matrix allocator and supporting changes, which is used to replace the x86 vector allocator which comes with separate pull request. This allows to replace the convoluted nested loop allocation function in x86 with a facility which supports the recently added property of managed interrupts proper and allows to switch to a best effort vector reservation scheme, which addresses problems with vector exhaustion. - A large update to the ARM GIC-V3-ITS driver adding support for range selectors. - New interrupt controllers: - Meson and Meson8 GPIO - BCM7271 L2 - Socionext EXIU If you expected that this will stop at some point, I have to disappoint you. There are new ones posted already. Sigh! - STM32 interrupt controller support for new platforms. - A pile of fixes, cleanups and updates to the MIPS GIC driver - The usual small fixes, cleanups and updates all over the place. Most visible one is to move the irq chip drivers Kconfig switches into a separate Kconfig menu" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (70 commits) genirq: Fix type of shifting literal 1 in __setup_irq() irqdomain: Drop pointless NULL check in virq_debug_show_one genirq/proc: Return proper error code when irq_set_affinity() fails irq/work: Use llist_for_each_entry_safe irqchip: mips-gic: Print warning if inherited GIC base is used irqchip/mips-gic: Add pr_fmt and reword pr_* messages irqchip/stm32: Move the wakeup on interrupt mask irqchip/stm32: Fix initial values irqchip/stm32: Add stm32h7 support dt-bindings/interrupt-controllers: Add compatible string for stm32h7 irqchip/stm32: Add multi-bank management irqchip/stm32: Select GENERIC_IRQ_CHIP irqchip/exiu: Add support for Socionext Synquacer EXIU controller dt-bindings: Add description of Socionext EXIU interrupt controller irqchip/gic-v3-its: Fix VPE activate callback return value irqchip: mips-gic: Make IPI bitmaps static irqchip: mips-gic: Share register writes in gic_set_type() irqchip: mips-gic: Remove gic_vpes variable irqchip: mips-gic: Use num_possible_cpus() to reserve IPIs irqchip: mips-gic: Configure EIC when CPUs come online ...
187 lines
4.6 KiB
C
187 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/kernel/irq/autoprobe.c
|
|
*
|
|
* Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
|
|
*
|
|
* This file contains the interrupt probing code and driver APIs.
|
|
*/
|
|
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/async.h>
|
|
|
|
#include "internals.h"
|
|
|
|
/*
|
|
* Autodetection depends on the fact that any interrupt that
|
|
* comes in on to an unassigned handler will get stuck with
|
|
* "IRQS_WAITING" cleared and the interrupt disabled.
|
|
*/
|
|
static DEFINE_MUTEX(probing_active);
|
|
|
|
/**
|
|
* probe_irq_on - begin an interrupt autodetect
|
|
*
|
|
* Commence probing for an interrupt. The interrupts are scanned
|
|
* and a mask of potential interrupt lines is returned.
|
|
*
|
|
*/
|
|
unsigned long probe_irq_on(void)
|
|
{
|
|
struct irq_desc *desc;
|
|
unsigned long mask = 0;
|
|
int i;
|
|
|
|
/*
|
|
* quiesce the kernel, or at least the asynchronous portion
|
|
*/
|
|
async_synchronize_full();
|
|
mutex_lock(&probing_active);
|
|
/*
|
|
* something may have generated an irq long ago and we want to
|
|
* flush such a longstanding irq before considering it as spurious.
|
|
*/
|
|
for_each_irq_desc_reverse(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (!desc->action && irq_settings_can_probe(desc)) {
|
|
/*
|
|
* Some chips need to know about probing in
|
|
* progress:
|
|
*/
|
|
if (desc->irq_data.chip->irq_set_type)
|
|
desc->irq_data.chip->irq_set_type(&desc->irq_data,
|
|
IRQ_TYPE_PROBE);
|
|
irq_activate_and_startup(desc, IRQ_NORESEND);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
/* Wait for longstanding interrupts to trigger. */
|
|
msleep(20);
|
|
|
|
/*
|
|
* enable any unassigned irqs
|
|
* (we must startup again here because if a longstanding irq
|
|
* happened in the previous stage, it may have masked itself)
|
|
*/
|
|
for_each_irq_desc_reverse(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (!desc->action && irq_settings_can_probe(desc)) {
|
|
desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;
|
|
if (irq_startup(desc, IRQ_NORESEND, IRQ_START_FORCE))
|
|
desc->istate |= IRQS_PENDING;
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
/*
|
|
* Wait for spurious interrupts to trigger
|
|
*/
|
|
msleep(100);
|
|
|
|
/*
|
|
* Now filter out any obviously spurious interrupts
|
|
*/
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
/* It triggered already - consider it spurious. */
|
|
if (!(desc->istate & IRQS_WAITING)) {
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
} else
|
|
if (i < 32)
|
|
mask |= 1 << i;
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_on);
|
|
|
|
/**
|
|
* probe_irq_mask - scan a bitmap of interrupt lines
|
|
* @val: mask of interrupts to consider
|
|
*
|
|
* Scan the interrupt lines and return a bitmap of active
|
|
* autodetect interrupts. The interrupt probe logic state
|
|
* is then returned to its previous value.
|
|
*
|
|
* Note: we need to scan all the irq's even though we will
|
|
* only return autodetect irq numbers - just so that we reset
|
|
* them all to a known state.
|
|
*/
|
|
unsigned int probe_irq_mask(unsigned long val)
|
|
{
|
|
unsigned int mask = 0;
|
|
struct irq_desc *desc;
|
|
int i;
|
|
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
if (i < 16 && !(desc->istate & IRQS_WAITING))
|
|
mask |= 1 << i;
|
|
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
mutex_unlock(&probing_active);
|
|
|
|
return mask & val;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_mask);
|
|
|
|
/**
|
|
* probe_irq_off - end an interrupt autodetect
|
|
* @val: mask of potential interrupts (unused)
|
|
*
|
|
* Scans the unused interrupt lines and returns the line which
|
|
* appears to have triggered the interrupt. If no interrupt was
|
|
* found then zero is returned. If more than one interrupt is
|
|
* found then minus the first candidate is returned to indicate
|
|
* their is doubt.
|
|
*
|
|
* The interrupt probe logic state is returned to its previous
|
|
* value.
|
|
*
|
|
* BUGS: When used in a module (which arguably shouldn't happen)
|
|
* nothing prevents two IRQ probe callers from overlapping. The
|
|
* results of this are non-optimal.
|
|
*/
|
|
int probe_irq_off(unsigned long val)
|
|
{
|
|
int i, irq_found = 0, nr_of_irqs = 0;
|
|
struct irq_desc *desc;
|
|
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
if (!(desc->istate & IRQS_WAITING)) {
|
|
if (!nr_of_irqs)
|
|
irq_found = i;
|
|
nr_of_irqs++;
|
|
}
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
mutex_unlock(&probing_active);
|
|
|
|
if (nr_of_irqs > 1)
|
|
irq_found = -irq_found;
|
|
|
|
return irq_found;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_off);
|
|
|