summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/IRQ-domain.txt3
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/brcm,l2-intc.txt29
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt (renamed from Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt)0
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/mach-iop13xx/include/mach/irqs.h2
-rw-r--r--arch/arm/mach-iop13xx/include/mach/time.h3
-rw-r--r--arch/arm/mach-iop13xx/iq81340mc.c1
-rw-r--r--arch/arm/mach-iop13xx/iq81340sc.c1
-rw-r--r--arch/arm/mach-iop13xx/msi.c52
-rw-r--r--arch/arm/mach-iop13xx/setup.c1
-rw-r--r--arch/arm/mach-iop13xx/tpmi.c1
-rw-r--r--arch/ia64/Kconfig1
-rw-r--r--arch/ia64/include/asm/hw_irq.h1
-rw-r--r--arch/ia64/include/asm/irq.h3
-rw-r--r--arch/ia64/include/asm/irq_remapping.h2
-rw-r--r--arch/ia64/kernel/iosapic.c2
-rw-r--r--arch/ia64/kernel/irq_ia64.c15
-rw-r--r--arch/mips/pci/msi-xlp.c10
-rw-r--r--arch/mips/pci/pci-xlr.c10
-rw-r--r--arch/s390/kernel/irq.c5
-rw-r--r--arch/s390/pci/pci.c6
-rw-r--r--arch/tile/Kconfig2
-rw-r--r--arch/tile/include/asm/irq.h6
-rw-r--r--arch/tile/kernel/irq.c40
-rw-r--r--arch/tile/kernel/pci_gx.c17
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/include/asm/io_apic.h2
-rw-r--r--arch/x86/include/asm/irq_remapping.h3
-rw-r--r--arch/x86/kernel/apic/io_apic.c130
-rw-r--r--arch/x86/kernel/hpet.c5
-rw-r--r--arch/x86/platform/uv/uv_irq.c10
-rw-r--r--drivers/iommu/dmar.c8
-rw-r--r--drivers/iommu/irq_remapping.c12
-rw-r--r--drivers/irqchip/Kconfig6
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c202
-rw-r--r--drivers/irqchip/irq-gic.c2
-rw-r--r--drivers/net/ethernet/tile/tilegx.c6
-rw-r--r--drivers/pci/htirq.c13
-rw-r--r--drivers/sh/intc/core.c6
-rw-r--r--drivers/tty/hvc/hvc_tile.c8
-rw-r--r--drivers/tty/serial/tilegx.c8
-rw-r--r--drivers/usb/host/ehci-tilegx.c8
-rw-r--r--drivers/usb/host/ohci-tilegx.c8
-rw-r--r--drivers/xen/events/events_base.c17
-rw-r--r--include/linux/interrupt.h46
-rw-r--r--include/linux/irq.h38
-rw-r--r--include/linux/irqchip/arm-gic.h2
-rw-r--r--include/linux/irqdesc.h4
-rw-r--r--kernel/irq/Kconfig9
-rw-r--r--kernel/irq/chip.c5
-rw-r--r--kernel/irq/internals.h8
-rw-r--r--kernel/irq/irqdesc.c95
-rw-r--r--kernel/irq/irqdomain.c6
-rw-r--r--kernel/irq/manage.c4
-rw-r--r--kernel/irq/spurious.c106
56 files changed, 597 insertions, 396 deletions
diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt
index 03df71a..8a8b82c 100644
--- a/Documentation/IRQ-domain.txt
+++ b/Documentation/IRQ-domain.txt
@@ -41,8 +41,7 @@ An interrupt controller driver creates and registers an irq_domain by
calling one of the irq_domain_add_*() functions (each mapping method
has a different allocator function, more on that later). The function
will return a pointer to the irq_domain on success. The caller must
-provide the allocator function with an irq_domain_ops structure with
-the .map callback populated as a minimum.
+provide the allocator function with an irq_domain_ops structure.
In most cases, the irq_domain will begin empty without any mappings
between hwirq and IRQ numbers. Mappings are added to the irq_domain
diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,l2-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,l2-intc.txt
new file mode 100644
index 0000000..448273a
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,l2-intc.txt
@@ -0,0 +1,29 @@
+Broadcom Generic Level 2 Interrupt Controller
+
+Required properties:
+
+- compatible: should be "brcm,l2-intc"
+- reg: specifies the base physical address and size of the registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an
+ interrupt source. Should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller
+ this controller is cacaded from
+- interrupts: specifies the interrupt line in the interrupt-parent irq space
+ to be used for cascading
+
+Optional properties:
+
+- brcm,irq-can-wake: If present, this means the L2 controller can be used as a
+ wakeup source for system suspend/resume.
+
+Example:
+
+hif_intr2_intc: interrupt-controller@f0441000 {
+ compatible = "brcm,l2-intc";
+ reg = <0xf0441000 0x30>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&intc>;
+ interrupts = <0x0 0x20 0x0>;
+};
diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt
index 5fc0313..5fc0313 100644
--- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 071dce7..ad89a03 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -480,6 +480,7 @@ config ARCH_IOP13XX
select PCI
select PLAT_IOP
select VMSPLIT_1G
+ select SPARSE_IRQ
help
Support for Intel's IOP13XX (XScale) family of processors.
diff --git a/arch/arm/mach-iop13xx/include/mach/irqs.h b/arch/arm/mach-iop13xx/include/mach/irqs.h
index 054e7ac..e8d24d3 100644
--- a/arch/arm/mach-iop13xx/include/mach/irqs.h
+++ b/arch/arm/mach-iop13xx/include/mach/irqs.h
@@ -191,6 +191,4 @@ static inline u32 read_intpnd_3(void)
#define NR_IOP13XX_IRQS (IRQ_IOP13XX_HPI + 1)
#endif
-#define NR_IRQS NR_IOP13XX_IRQS
-
#endif /* _IOP13XX_IRQ_H_ */
diff --git a/arch/arm/mach-iop13xx/include/mach/time.h b/arch/arm/mach-iop13xx/include/mach/time.h
index f1c00d6..15bc9bb 100644
--- a/arch/arm/mach-iop13xx/include/mach/time.h
+++ b/arch/arm/mach-iop13xx/include/mach/time.h
@@ -1,5 +1,8 @@
#ifndef _IOP13XX_TIME_H_
#define _IOP13XX_TIME_H_
+
+#include <mach/irqs.h>
+
#define IRQ_IOP_TIMER0 IRQ_IOP13XX_TIMER0
#define IOP_TMR_EN 0x02
diff --git a/arch/arm/mach-iop13xx/iq81340mc.c b/arch/arm/mach-iop13xx/iq81340mc.c
index 02a8228..9cd07d3 100644
--- a/arch/arm/mach-iop13xx/iq81340mc.c
+++ b/arch/arm/mach-iop13xx/iq81340mc.c
@@ -93,4 +93,5 @@ MACHINE_START(IQ81340MC, "Intel IQ81340MC")
.init_time = iq81340mc_timer_init,
.init_machine = iq81340mc_init,
.restart = iop13xx_restart,
+ .nr_irqs = NR_IOP13XX_IRQS,
MACHINE_END
diff --git a/arch/arm/mach-iop13xx/iq81340sc.c b/arch/arm/mach-iop13xx/iq81340sc.c
index 1b80f10..b3ec11c 100644
--- a/arch/arm/mach-iop13xx/iq81340sc.c
+++ b/arch/arm/mach-iop13xx/iq81340sc.c
@@ -95,4 +95,5 @@ MACHINE_START(IQ81340SC, "Intel IQ81340SC")
.init_time = iq81340sc_timer_init,
.init_machine = iq81340sc_init,
.restart = iop13xx_restart,
+ .nr_irqs = NR_IOP13XX_IRQS,
MACHINE_END
diff --git a/arch/arm/mach-iop13xx/msi.c b/arch/arm/mach-iop13xx/msi.c
index 560d5b2..e7730cf 100644
--- a/arch/arm/mach-iop13xx/msi.c
+++ b/arch/arm/mach-iop13xx/msi.c
@@ -23,10 +23,7 @@
#include <linux/msi.h>
#include <asm/mach/irq.h>
#include <asm/irq.h>
-
-
-#define IOP13XX_NUM_MSI_IRQS 128
-static DECLARE_BITMAP(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
+#include <mach/irqs.h>
/* IMIPR0 CP6 R8 Page 1
*/
@@ -121,41 +118,6 @@ void __init iop13xx_msi_init(void)
irq_set_chained_handler(IRQ_IOP13XX_INBD_MSI, iop13xx_msi_handler);
}
-/*
- * Dynamic irq allocate and deallocation
- */
-int create_irq(void)
-{
- int irq, pos;
-
-again:
- pos = find_first_zero_bit(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
- irq = IRQ_IOP13XX_MSI_0 + pos;
- if (irq > NR_IRQS)
- return -ENOSPC;
- /* test_and_set_bit operates on 32-bits at a time */
- if (test_and_set_bit(pos, msi_irq_in_use))
- goto again;
-
- dynamic_irq_init(irq);
-
- return irq;
-}
-
-void destroy_irq(unsigned int irq)
-{
- int pos = irq - IRQ_IOP13XX_MSI_0;
-
- dynamic_irq_cleanup(irq);
-
- clear_bit(pos, msi_irq_in_use);
-}
-
-void arch_teardown_msi_irq(unsigned int irq)
-{
- destroy_irq(irq);
-}
-
static void iop13xx_msi_nop(struct irq_data *d)
{
return;
@@ -172,12 +134,17 @@ static struct irq_chip iop13xx_msi_chip = {
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
- int id, irq = create_irq();
+ int id, irq = irq_alloc_desc_from(IRQ_IOP13XX_MSI_0, -1);
struct msi_msg msg;
if (irq < 0)
return irq;
+ if (irq >= NR_IOP13XX_IRQS) {
+ irq_free_desc(irq);
+ return -ENOSPC;
+ }
+
irq_set_msi_desc(irq, desc);
msg.address_hi = 0x0;
@@ -191,3 +158,8 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
return 0;
}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+ irq_free_desc(irq);
+}
diff --git a/arch/arm/mach-iop13xx/setup.c b/arch/arm/mach-iop13xx/setup.c
index 96e6c7a..bca96f4 100644
--- a/arch/arm/mach-iop13xx/setup.c
+++ b/arch/arm/mach-iop13xx/setup.c
@@ -27,6 +27,7 @@
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/hardware/iop_adma.h>
+#include <mach/irqs.h>
#define IOP13XX_UART_XTAL 33334000
#define IOP13XX_SETUP_DEBUG 0
diff --git a/arch/arm/mach-iop13xx/tpmi.c b/arch/arm/mach-iop13xx/tpmi.c
index 6fdad7a..db511ec 100644
--- a/arch/arm/mach-iop13xx/tpmi.c
+++ b/arch/arm/mach-iop13xx/tpmi.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <asm/irq.h>
#include <asm/sizes.h>
+#include <mach/irqs.h>
/* assumes CONTROLLER_ONLY# is never asserted in the ESSR register */
#define IOP13XX_TPMI_MMR(dev) IOP13XX_REG_ADDR32_PHYS(0x48000 + (dev << 12))
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 12c3afe..2f3abcf 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -32,6 +32,7 @@ config IA64
select GENERIC_IRQ_PROBE
select GENERIC_PENDING_IRQ if SMP
select GENERIC_IRQ_SHOW
+ select GENERIC_IRQ_LEGACY
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_IOMAP
diff --git a/arch/ia64/include/asm/hw_irq.h b/arch/ia64/include/asm/hw_irq.h
index a681d02..029bab3 100644
--- a/arch/ia64/include/asm/hw_irq.h
+++ b/arch/ia64/include/asm/hw_irq.h
@@ -132,7 +132,6 @@ extern int reserve_irq_vector (int vector);
extern void __setup_vector_irq(int cpu);
extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect);
extern void ia64_native_register_percpu_irq (ia64_vector vec, struct irqaction *action);
-extern int check_irq_used (int irq);
extern void destroy_and_reserve_irq (unsigned int irq);
#if defined(CONFIG_SMP) && (defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_DIG))
diff --git a/arch/ia64/include/asm/irq.h b/arch/ia64/include/asm/irq.h
index 91b920fd..820667c 100644
--- a/arch/ia64/include/asm/irq.h
+++ b/arch/ia64/include/asm/irq.h
@@ -31,4 +31,7 @@ bool is_affinity_mask_valid(const struct cpumask *cpumask);
#define is_affinity_mask_valid is_affinity_mask_valid
+int create_irq(void);
+void destroy_irq(unsigned int irq);
+
#endif /* _ASM_IA64_IRQ_H */
diff --git a/arch/ia64/include/asm/irq_remapping.h b/arch/ia64/include/asm/irq_remapping.h
index a8687b1..e3b3556 100644
--- a/arch/ia64/include/asm/irq_remapping.h
+++ b/arch/ia64/include/asm/irq_remapping.h
@@ -1,4 +1,6 @@
#ifndef __IA64_INTR_REMAPPING_H
#define __IA64_INTR_REMAPPING_H
#define irq_remapping_enabled 0
+#define dmar_alloc_hwirq create_irq
+#define dmar_free_hwirq destroy_irq
#endif
diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
index 19f107b..cd44a57 100644
--- a/arch/ia64/kernel/iosapic.c
+++ b/arch/ia64/kernel/iosapic.c
@@ -735,7 +735,7 @@ iosapic_register_intr (unsigned int gsi,
rte = find_rte(irq, gsi);
if(iosapic_intr_info[irq].count == 0) {
assign_irq_vector(irq);
- dynamic_irq_init(irq);
+ irq_init_desc(irq);
} else if (rte->refcnt != NO_REF_RTE) {
rte->refcnt++;
goto unlock_iosapic_lock;
diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c
index 0884f5e..03ea78e 100644
--- a/arch/ia64/kernel/irq_ia64.c
+++ b/arch/ia64/kernel/irq_ia64.c
@@ -93,14 +93,6 @@ static int irq_status[NR_IRQS] = {
[0 ... NR_IRQS -1] = IRQ_UNUSED
};
-int check_irq_used(int irq)
-{
- if (irq_status[irq] == IRQ_USED)
- return 1;
-
- return -1;
-}
-
static inline int find_unassigned_irq(void)
{
int irq;
@@ -390,8 +382,7 @@ void destroy_and_reserve_irq(unsigned int irq)
{
unsigned long flags;
- dynamic_irq_cleanup(irq);
-
+ irq_init_desc(irq);
spin_lock_irqsave(&vector_lock, flags);
__clear_irq_vector(irq);
irq_status[irq] = IRQ_RSVD;
@@ -424,13 +415,13 @@ int create_irq(void)
out:
spin_unlock_irqrestore(&vector_lock, flags);
if (irq >= 0)
- dynamic_irq_init(irq);
+ irq_init_desc(irq);
return irq;
}
void destroy_irq(unsigned int irq)
{
- dynamic_irq_cleanup(irq);
+ irq_init_desc(irq);
clear_irq_vector(irq);
}
diff --git a/arch/mips/pci/msi-xlp.c b/arch/mips/pci/msi-xlp.c
index afd8405..3249685 100644
--- a/arch/mips/pci/msi-xlp.c
+++ b/arch/mips/pci/msi-xlp.c
@@ -206,14 +206,8 @@ static struct irq_chip xlp_msix_chip = {
.irq_unmask = unmask_msi_irq,
};
-void destroy_irq(unsigned int irq)
-{
- /* nothing to do yet */
-}
-
void arch_teardown_msi_irq(unsigned int irq)
{
- destroy_irq(irq);
}
/*
@@ -298,10 +292,8 @@ static int xlp_setup_msi(uint64_t lnkbase, int node, int link,
xirq = xirq + msivec; /* msi mapped to global irq space */
ret = irq_set_msi_desc(xirq, desc);
- if (ret < 0) {
- destroy_irq(xirq);
+ if (ret < 0)
return ret;
- }
write_msi_msg(xirq, &msg);
return 0;
diff --git a/arch/mips/pci/pci-xlr.c b/arch/mips/pci/pci-xlr.c
index 4427abb..0dde803 100644
--- a/arch/mips/pci/pci-xlr.c
+++ b/arch/mips/pci/pci-xlr.c
@@ -214,14 +214,8 @@ static int get_irq_vector(const struct pci_dev *dev)
}
#ifdef CONFIG_PCI_MSI
-void destroy_irq(unsigned int irq)
-{
- /* nothing to do yet */
-}
-
void arch_teardown_msi_irq(unsigned int irq)
{
- destroy_irq(irq);
}
int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
@@ -263,10 +257,8 @@ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
MSI_DATA_DELIVERY_FIXED;
ret = irq_set_msi_desc(irq, desc);
- if (ret < 0) {
- destroy_irq(irq);
+ if (ret < 0)
return ret;
- }
write_msi_msg(irq, &msg);
return 0;
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index c7463aa..99b0b09 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -92,7 +92,6 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = {
void __init init_IRQ(void)
{
- irq_reserve_irqs(0, THIN_INTERRUPT);
init_cio_interrupts();
init_airq_interrupts();
init_ext_interrupts();
@@ -151,9 +150,9 @@ out:
return 0;
}
-int arch_show_interrupts(struct seq_file *p, int prec)
+unsigned int arch_dynirq_lower_bound(unsigned int from)
{
- return 0;
+ return from < THIN_INTERRUPT ? THIN_INTERRUPT : from;
}
/*
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index bdf0257..9ddc51e 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -401,11 +401,11 @@ static void zpci_irq_handler(struct airq_struct *airq)
int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
struct zpci_dev *zdev = get_zdev(pdev);
- unsigned int hwirq, irq, msi_vecs;
+ unsigned int hwirq, msi_vecs;
unsigned long aisb;
struct msi_desc *msi;
struct msi_msg msg;
- int rc;
+ int rc, irq;
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
@@ -433,7 +433,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
list_for_each_entry(msi, &pdev->msi_list, list) {
rc = -EIO;
irq = irq_alloc_desc(0); /* Alloc irq on node 0 */
- if (irq == NO_IRQ)
+ if (irq < 0)
goto out_msi;
rc = irq_set_msi_desc(irq, msi);
if (rc)
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 85258ca..4f3006b 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -125,6 +125,8 @@ config HVC_TILE
config TILEGX
bool "Building for TILE-Gx (64-bit) processor"
+ select SPARSE_IRQ
+ select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
select HAVE_FUNCTION_GRAPH_TRACER
diff --git a/arch/tile/include/asm/irq.h b/arch/tile/include/asm/irq.h
index 33cff9a..1fe8691 100644
--- a/arch/tile/include/asm/irq.h
+++ b/arch/tile/include/asm/irq.h
@@ -18,10 +18,12 @@
#include <linux/hardirq.h>
/* The hypervisor interface provides 32 IRQs. */
-#define NR_IRQS 32
+#define NR_IRQS 32
/* IRQ numbers used for linux IPIs. */
-#define IRQ_RESCHEDULE 0
+#define IRQ_RESCHEDULE 0
+/* Interrupts for dynamic allocation start at 1. Let the core allocate irq0 */
+#define NR_IRQS_LEGACY 1
#define irq_canonicalize(irq) (irq)
diff --git a/arch/tile/kernel/irq.c b/arch/tile/kernel/irq.c
index 906a76b..637f2ff 100644
--- a/arch/tile/kernel/irq.c
+++ b/arch/tile/kernel/irq.c
@@ -54,13 +54,6 @@ static DEFINE_PER_CPU(unsigned long, irq_disable_mask)
*/
static DEFINE_PER_CPU(int, irq_depth);
-/* State for allocating IRQs on Gx. */
-#if CHIP_HAS_IPI()
-static unsigned long available_irqs = ((1UL << NR_IRQS) - 1) &
- (~(1UL << IRQ_RESCHEDULE));
-static DEFINE_SPINLOCK(available_irqs_lock);
-#endif
-
#if CHIP_HAS_IPI()
/* Use SPRs to manipulate device interrupts. */
#define mask_irqs(irq_mask) __insn_mtspr(SPR_IPI_MASK_SET_K, irq_mask)
@@ -278,38 +271,11 @@ int arch_show_interrupts(struct seq_file *p, int prec)
return 0;
}
-/*
- * Generic, controller-independent functions:
- */
-
#if CHIP_HAS_IPI()
-int create_irq(void)
+int arch_setup_hwirq(unsigned int irq, int node)
{
- unsigned long flags;
- int result;
-
- spin_lock_irqsave(&available_irqs_lock, flags);
- if (available_irqs == 0)
- result = -ENOMEM;
- else {
- result = __ffs(available_irqs);
- available_irqs &= ~(1UL << result);
- dynamic_irq_init(result);
- }
- spin_unlock_irqrestore(&available_irqs_lock, flags);
-
- return result;
+ return irq >= NR_IRQS ? -EINVAL : 0;
}
-EXPORT_SYMBOL(create_irq);
-void destroy_irq(unsigned int irq)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&available_irqs_lock, flags);
- available_irqs |= (1UL << irq);
- dynamic_irq_cleanup(irq);
- spin_unlock_irqrestore(&available_irqs_lock, flags);
-}
-EXPORT_SYMBOL(destroy_irq);
+void arch_teardown_hwirq(unsigned int irq) { }
#endif
diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c
index 077b7bc..e39f9c5 100644
--- a/arch/tile/kernel/pci_gx.c
+++ b/arch/tile/kernel/pci_gx.c
@@ -350,10 +350,9 @@ static int tile_init_irqs(struct pci_controller *controller)
int cpu;
/* Ask the kernel to allocate an IRQ. */
- irq = create_irq();
- if (irq < 0) {
+ irq = irq_alloc_hwirq(-1);
+ if (!irq) {
pr_err("PCI: no free irq vectors, failed for %d\n", i);
-
goto free_irqs;
}
controller->irq_intx_table[i] = irq;
@@ -382,7 +381,7 @@ static int tile_init_irqs(struct pci_controller *controller)
free_irqs:
for (j = 0; j < i; j++)
- destroy_irq(controller->irq_intx_table[j]);
+ irq_free_hwirq(controller->irq_intx_table[j]);
return -1;
}
@@ -1500,9 +1499,9 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
int irq;
int ret;
- irq = create_irq();
- if (irq < 0)
- return irq;
+ irq = irq_alloc_hwirq(-1);
+ if (!irq)
+ return -ENOSPC;
/*
* Since we use a 64-bit Mem-Map to accept the MSI write, we fail
@@ -1601,11 +1600,11 @@ hv_msi_config_failure:
/* Free mem-map */
msi_mem_map_alloc_failure:
is_64_failure:
- destroy_irq(irq);
+ irq_free_hwirq(irq);
return ret;
}
void arch_teardown_msi_irq(unsigned int irq)
{
- destroy_irq(irq);
+ irq_free_hwirq(irq);
}
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7d5feb5..7a01d433 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -833,6 +833,7 @@ config X86_LOCAL_APIC
config X86_IO_APIC
def_bool y
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_IOAPIC || PCI_MSI
+ select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
bool "Reroute for broken boot IRQs"
diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h
index 459e50a..90f97b4 100644
--- a/arch/x86/include/asm/io_apic.h
+++ b/arch/x86/include/asm/io_apic.h
@@ -168,8 +168,6 @@ extern int save_ioapic_entries(void);
extern void mask_ioapic_entries(void);
extern int restore_ioapic_entries(void);
-extern int get_nr_irqs_gsi(void);
-
extern void setup_ioapic_ids_from_mpc(void);
extern void setup_ioapic_ids_from_mpc_nocheck(void);
diff --git a/arch/x86/include/asm/irq_remapping.h b/arch/x86/include/asm/irq_remapping.h
index d806b22..b7747c4 100644
--- a/arch/x86/include/asm/irq_remapping.h
+++ b/arch/x86/include/asm/irq_remapping.h
@@ -103,4 +103,7 @@ static inline bool setup_remapped_irq(int irq,
}
#endif /* CONFIG_IRQ_REMAP */
+#define dmar_alloc_hwirq() irq_alloc_hwirq(-1)
+#define dmar_free_hwirq irq_free_hwirq
+
#endif /* __X86_IRQ_REMAPPING_H */
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 992060e..9d0a979 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -206,9 +206,6 @@ int __init arch_early_irq_init(void)
count = ARRAY_SIZE(irq_cfgx);
node = cpu_to_node(0);
- /* Make sure the legacy interrupts are marked in the bitmap */
- irq_reserve_irqs(0, legacy_pic->nr_legacy_irqs);
-
for (i = 0; i < count; i++) {
irq_set_chip_data(i, &cfg[i]);
zalloc_cpumask_var_node(&cfg[i].domain, GFP_KERNEL, node);
@@ -281,18 +278,6 @@ static struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node)
return cfg;
}
-static int alloc_irqs_from(unsigned int from, unsigned int count, int node)
-{
- return irq_alloc_descs_from(from, count, node);
-}
-
-static void free_irq_at(unsigned int at, struct irq_cfg *cfg)
-{
- free_irq_cfg(at, cfg);
- irq_free_desc(at);
-}
-
-
struct io_apic {
unsigned int index;
unsigned int unused[3];
@@ -2916,98 +2901,39 @@ static int __init ioapic_init_ops(void)
device_initcall(ioapic_init_ops);
/*
- * Dynamic irq allocate and deallocation
+ * Dynamic irq allocate and deallocation. Should be replaced by irq domains!
*/
-unsigned int __create_irqs(unsigned int from, unsigned int count, int node)
+int arch_setup_hwirq(unsigned int irq, int node)
{
- struct irq_cfg **cfg;
+ struct irq_cfg *cfg;
unsigned long flags;
- int irq, i;
-
- if (from < nr_irqs_gsi)
- from = nr_irqs_gsi;
+ int ret;
- cfg = kzalloc_node(count * sizeof(cfg[0]), GFP_KERNEL, node);
+ cfg = alloc_irq_cfg(irq, node);
if (!cfg)
- return 0;
-
- irq = alloc_irqs_from(from, count, node);
- if (irq < 0)
- goto out_cfgs;
-
- for (i = 0; i < count; i++) {
- cfg[i] = alloc_irq_cfg(irq + i, node);
- if (!cfg[i])
- goto out_irqs;
- }
+ return -ENOMEM;
raw_spin_lock_irqsave(&vector_lock, flags);
- for (i = 0; i < count; i++)
- if (__assign_irq_vector(irq + i, cfg[i], apic->target_cpus()))
- goto out_vecs;
- raw_spin_unlock_irqrestore(&vector_lock, flags);
-
- for (i = 0; i < count; i++) {
- irq_set_chip_data(irq + i, cfg[i]);
- irq_clear_status_flags(irq + i, IRQ_NOREQUEST);
- }
-
- kfree(cfg);
- return irq;
-
-out_vecs:
- for (i--; i >= 0; i--)
- __clear_irq_vector(irq + i, cfg[i]);
+ ret = __assign_irq_vector(irq, cfg, apic->target_cpus());
raw_spin_unlock_irqrestore(&vector_lock, flags);
-out_irqs:
- for (i = 0; i < count; i++)
- free_irq_at(irq + i, cfg[i]);
-out_cfgs:
- kfree(cfg);
- return 0;
-}
-unsigned int create_irq_nr(unsigned int from, int node)
-{
- return __create_irqs(from, 1, node);
-}
-
-int create_irq(void)
-{
- int node = cpu_to_node(0);
- unsigned int irq_want;
- int irq;
-
- irq_want = nr_irqs_gsi;
- irq = create_irq_nr(irq_want, node);
-
- if (irq == 0)
- irq = -1;
-
- return irq;
+ if (!ret)
+ irq_set_chip_data(irq, cfg);
+ else
+ free_irq_cfg(irq, cfg);
+ return ret;
}
-void destroy_irq(unsigned int irq)
+void arch_teardown_hwirq(unsigned int irq)
{
struct irq_cfg *cfg = irq_get_chip_data(irq);
unsigned long flags;
- irq_set_status_flags(irq, IRQ_NOREQUEST|IRQ_NOPROBE);
-
free_remapped_irq(irq);
-
raw_spin_lock_irqsave(&vector_lock, flags);
__clear_irq_vector(irq, cfg);
raw_spin_unlock_irqrestore(&vector_lock, flags);
- free_irq_at(irq, cfg);
-}
-
-void destroy_irqs(unsigned int irq, unsigned int count)
-{
- unsigned int i;
-
- for (i = 0; i < count; i++)
- destroy_irq(irq + i);
+ free_irq_cfg(irq, cfg);
}
/*
@@ -3136,8 +3062,8 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
- unsigned int irq, irq_want;
struct msi_desc *msidesc;
+ unsigned int irq;
int node, ret;
/* Multiple MSI vectors only supported with interrupt remapping */
@@ -3145,28 +3071,25 @@ int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
return 1;
node = dev_to_node(&dev->dev);
- irq_want = nr_irqs_gsi;
+
list_for_each_entry(msidesc, &dev->msi_list, list) {
- irq = create_irq_nr(irq_want, node);
- if (irq == 0)
+ irq = irq_alloc_hwirq(node);
+ if (!irq)
return -ENOSPC;
- irq_want = irq + 1;
-
ret = setup_msi_irq(dev, msidesc, irq, 0);
- if (ret < 0)
- goto error;
+ if (ret < 0) {
+ irq_free_hwirq(irq);
+ return ret;
+ }
+
}
return 0;
-
-error:
- destroy_irq(irq);
- return ret;
}
void native_teardown_msi_irq(unsigned int irq)
{
- destroy_irq(irq);
+ irq_free_hwirq(irq);
}
#ifdef CONFIG_DMAR_TABLE
@@ -3420,11 +3343,6 @@ static void __init probe_nr_irqs_gsi(void)
printk(KERN_DEBUG "nr_irqs_gsi: %d\n", nr_irqs_gsi);
}
-int get_nr_irqs_gsi(void)
-{
- return nr_irqs_gsi;
-}
-
unsigned int arch_dynirq_lower_bound(unsigned int from)
{
return from < nr_irqs_gsi ? nr_irqs_gsi : from;
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 4177bfb..5f5a147 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -479,7 +479,7 @@ static int hpet_msi_next_event(unsigned long delta,
static int hpet_setup_msi_irq(unsigned int irq)
{
if (x86_msi.setup_hpet_msi(irq, hpet_blockid)) {
- destroy_irq(irq);
+ irq_free_hwirq(irq);
return -EINVAL;
}
return 0;
@@ -487,9 +487,8 @@ static int hpet_setup_msi_irq(unsigned int irq)
static int hpet_assign_irq(struct hpet_dev *dev)
{
- unsigned int irq;
+ unsigned int irq = irq_alloc_hwirq(-1);
- irq = create_irq_nr(0, -1);
if (!irq)
return -EINVAL;
diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c
index acf7752..b233681 100644
--- a/arch/x86/platform/uv/uv_irq.c
+++ b/arch/x86/platform/uv/uv_irq.c
@@ -238,11 +238,9 @@ uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
unsigned long mmr_offset, int limit)
{
- int irq, ret;
+ int ret, irq = irq_alloc_hwirq(uv_blade_to_memory_nid(mmr_blade));
- irq = create_irq_nr(NR_IRQS_LEGACY, uv_blade_to_memory_nid(mmr_blade));
-
- if (irq <= 0)
+ if (!irq)
return -EBUSY;
ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset,
@@ -250,7 +248,7 @@ int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
if (ret == irq)
uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade);
else
- destroy_irq(irq);
+ irq_free_hwirq(irq);
return ret;
}
@@ -285,6 +283,6 @@ void uv_teardown_irq(unsigned int irq)
n = n->rb_right;
}
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
- destroy_irq(irq);
+ irq_free_hwirq(irq);
}
EXPORT_SYMBOL_GPL(uv_teardown_irq);
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 39f8b71..9a4f05e 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -994,7 +994,7 @@ static void free_iommu(struct intel_iommu *iommu)
if (iommu->irq) {
free_irq(iommu->irq, iommu);
irq_set_handler_data(iommu->irq, NULL);
- destroy_irq(iommu->irq);
+ dmar_free_hwirq(iommu->irq);
}
if (iommu->qi) {
@@ -1550,8 +1550,8 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
if (iommu->irq)
return 0;
- irq = create_irq();
- if (!irq) {
+ irq = dmar_alloc_hwirq();
+ if (irq <= 0) {
pr_err("IOMMU: no free vectors\n");
return -EINVAL;
}
@@ -1563,7 +1563,7 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
if (ret) {
irq_set_handler_data(irq, NULL);
iommu->irq = 0;
- destroy_irq(irq);
+ dmar_free_hwirq(irq);
return ret;
}
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index 228632c9..33c4395 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -51,7 +51,7 @@ static void irq_remapping_disable_io_apic(void)
static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
{
- int node, ret, sub_handle, nvec_pow2, index = 0;
+ int ret, sub_handle, nvec_pow2, index = 0;
unsigned int irq;
struct msi_desc *msidesc;
@@ -61,8 +61,7 @@ static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
WARN_ON(msidesc->msi_attrib.multiple);
WARN_ON(msidesc->nvec_used);
- node = dev_to_node(&dev->dev);
- irq = __create_irqs(get_nr_irqs_gsi(), nvec, node);
+ irq = irq_alloc_hwirqs(nvec, dev_to_node(&dev->dev));
if (irq == 0)
return -ENOSPC;
@@ -89,7 +88,7 @@ static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
return 0;
error:
- destroy_irqs(irq, nvec);
+ irq_free_hwirqs(irq, nvec);
/*
* Restore altered MSI descriptor fields and prevent just destroyed
@@ -109,12 +108,11 @@ static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
unsigned int irq;
node = dev_to_node(&dev->dev);
- irq = get_nr_irqs_gsi();
sub_handle = 0;
list_for_each_entry(msidesc, &dev->msi_list, list) {
- irq = create_irq_nr(irq, node);
+ irq = irq_alloc_hwirq(node);
if (irq == 0)
return -1;
@@ -137,7 +135,7 @@ static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
return 0;
error:
- destroy_irq(irq);
+ irq_free_hwirq(irq);
return ret;
}
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d770f74..bbb746e 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -30,6 +30,12 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for
power management.
+config BRCMSTB_L2_IRQ
+ bool
+ depends on ARM
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config DW_APB_ICTL
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f180f8d..62a13e5 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
+obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
new file mode 100644
index 0000000..8ee2a36
--- /dev/null
+++ b/drivers/irqchip/irq-brcmstb-l2.c
@@ -0,0 +1,202 @@
+/*
+ * Generic Broadcom Set Top Box Level 2 Interrupt controller driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+/* Register offsets in the L2 interrupt controller */
+#define CPU_STATUS 0x00
+#define CPU_SET 0x04
+#define CPU_CLEAR 0x08
+#define CPU_MASK_STATUS 0x0c
+#define CPU_MASK_SET 0x10
+#define CPU_MASK_CLEAR 0x14
+
+/* L2 intc private data structure */
+struct brcmstb_l2_intc_data {
+ int parent_irq;
+ void __iomem *base;
+ struct irq_domain *domain;
+ bool can_wake;
+ u32 saved_mask; /* for suspend/resume */
+};
+
+static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
+{
+ struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+
+ status = __raw_readl(b->base + CPU_STATUS) &
+ ~(__raw_readl(b->base + CPU_MASK_STATUS));
+
+ if (status == 0) {
+ do_bad_IRQ(irq, desc);
+ goto out;
+ }
+
+ do {
+ irq = ffs(status) - 1;
+ /* ack at our level */
+ __raw_writel(1 << irq, b->base + CPU_CLEAR);
+ status &= ~(1 << irq);
+ generic_handle_irq(irq_find_mapping(b->domain, irq));
+ } while (status);
+out:
+ chained_irq_exit(chip, desc);
+}
+
+static void brcmstb_l2_intc_suspend(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct brcmstb_l2_intc_data *b = gc->private;
+
+ irq_gc_lock(gc);
+ /* Save the current mask */
+ b->saved_mask = __raw_readl(b->base + CPU_MASK_STATUS);
+
+ if (b->can_wake) {
+ /* Program the wakeup mask */
+ __raw_writel(~gc->wake_active, b->base + CPU_MASK_SET);
+ __raw_writel(gc->wake_active, b->base + CPU_MASK_CLEAR);
+ }
+ irq_gc_unlock(gc);
+}
+
+static void brcmstb_l2_intc_resume(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct brcmstb_l2_intc_data *b = gc->private;
+
+ irq_gc_lock(gc);
+ /* Clear unmasked non-wakeup interrupts */
+ __raw_writel(~b->saved_mask & ~gc->wake_active, b->base + CPU_CLEAR);
+
+ /* Restore the saved mask */
+ __raw_writel(b->saved_mask, b->base + CPU_MASK_SET);
+ __raw_writel(~b->saved_mask, b->base + CPU_MASK_CLEAR);
+ irq_gc_unlock(gc);
+}
+
+int __init brcmstb_l2_intc_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct brcmstb_l2_intc_data *data;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ int ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->base = of_iomap(np, 0);
+ if (!data->base) {
+ pr_err("failed to remap intc L2 registers\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ /* Disable all interrupts by default */
+ __raw_writel(0xffffffff, data->base + CPU_MASK_SET);
+ __raw_writel(0xffffffff, data->base + CPU_CLEAR);
+
+ data->parent_irq = irq_of_parse_and_map(np, 0);
+ if (data->parent_irq < 0) {
+ pr_err("failed to find parent interrupt\n");
+ ret = data->parent_irq;
+ goto out_unmap;
+ }
+
+ data->domain = irq_domain_add_linear(np, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!data->domain) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ /* Allocate a single Generic IRQ chip for this node */
+ ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
+ np->full_name, handle_level_irq, clr, 0, 0);
+ if (ret) {
+ pr_err("failed to allocate generic irq chip\n");
+ goto out_free_domain;
+ }
+
+ /* Set the IRQ chaining logic */
+ irq_set_handler_data(data->parent_irq, data);
+ irq_set_chained_handler(data->parent_irq, brcmstb_l2_intc_irq_handle);
+
+ gc = irq_get_domain_generic_chip(data->domain, 0);
+ gc->reg_base = data->base;
+ gc->private = data;
+ ct = gc->chip_types;
+
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->regs.ack = CPU_CLEAR;
+
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
+ ct->regs.disable = CPU_MASK_SET;
+
+ ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+ ct->regs.enable = CPU_MASK_CLEAR;
+
+ ct->chip.irq_suspend = brcmstb_l2_intc_suspend;
+ ct->chip.irq_resume = brcmstb_l2_intc_resume;
+
+ if (of_property_read_bool(np, "brcm,irq-can-wake")) {
+ data->can_wake = true;
+ /* This IRQ chip can wake the system, set all child interrupts
+ * in wake_enabled mask
+ */
+ gc->wake_enabled = 0xffffffff;
+ ct->chip.irq_set_wake = irq_gc_set_wake;
+ }
+
+ pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n",
+ data->base, data->parent_irq);
+
+ return 0;
+
+out_free_domain:
+ irq_domain_remove(data->domain);
+out_unmap:
+ iounmap(data->base);
+out_free:
+ kfree(data);
+ return ret;
+}
+IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_intc_of_init);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 57d165e..7e11c9d 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -291,7 +291,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
- irqnr = irqstat & ~0x1c00;
+ irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c
index 7e1c91d..449011b 100644
--- a/drivers/net/ethernet/tile/tilegx.c
+++ b/drivers/net/ethernet/tile/tilegx.c
@@ -1208,8 +1208,8 @@ static int tile_net_setup_interrupts(struct net_device *dev)
irq = md->ingress_irq;
if (irq < 0) {
- irq = create_irq();
- if (irq < 0) {
+ irq = irq_alloc_hwirq(-1);
+ if (!irq) {
netdev_err(dev,
"create_irq failed: mpipe[%d] %d\n",
instance, irq);
@@ -1223,7 +1223,7 @@ static int tile_net_setup_interrupts(struct net_device *dev)
if (rc != 0) {
netdev_err(dev, "request_irq failed: mpipe[%d] %d\n",
instance, rc);
- destroy_irq(irq);
+ irq_free_hwirq(irq);
return rc;
}
md->ingress_irq = irq;
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
index 6e373ea..d68b030 100644
--- a/drivers/pci/htirq.c
+++ b/drivers/pci/htirq.c
@@ -87,12 +87,9 @@ void unmask_ht_irq(struct irq_data *data)
int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
{
struct ht_irq_cfg *cfg;
+ int max_irq, pos, irq;
unsigned long flags;
u32 data;
- int max_irq;
- int pos;
- int irq;
- int node;
pos = pci_find_ht_capability(dev, HT_CAPTYPE_IRQ);
if (!pos)
@@ -120,10 +117,8 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
cfg->msg.address_lo = 0xffffffff;
cfg->msg.address_hi = 0xffffffff;
- node = dev_to_node(&dev->dev);
- irq = create_irq_nr(0, node);
-
- if (irq <= 0) {
+ irq = irq_alloc_hwirq(dev_to_node(&dev->dev));
+ if (!irq) {
kfree(cfg);
return -EBUSY;
}
@@ -166,7 +161,7 @@ void ht_destroy_irq(unsigned int irq)
cfg = irq_get_handler_data(irq);
irq_set_chip(irq, NULL);
irq_set_handler_data(irq, NULL);
- destroy_irq(irq);
+ irq_free_hwirq(irq);
kfree(cfg);
}
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 8f32a13..81f2298 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -80,12 +80,6 @@ static void __init intc_register_irq(struct intc_desc *desc,
unsigned int data[2], primary;
unsigned long flags;
- /*
- * Register the IRQ position with the global IRQ map, then insert
- * it in to the radix tree.
- */
- irq_reserve_irq(irq);
-
raw_spin_lock_irqsave(&intc_big_lock, flags);
radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));
raw_spin_unlock_irqrestore(&intc_big_lock, flags);
diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c
index af8cdaa..147d49e 100644
--- a/drivers/tty/hvc/hvc_tile.c
+++ b/drivers/tty/hvc/hvc_tile.c
@@ -133,14 +133,14 @@ static int hvc_tile_probe(struct platform_device *pdev)
int tile_hvc_irq;
/* Create our IRQ and register it. */
- tile_hvc_irq = create_irq();
- if (tile_hvc_irq < 0)
+ tile_hvc_irq = irq_alloc_hwirq(-1);
+ if (!tile_hvc_irq)
return -ENXIO;
tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU);
hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128);
if (IS_ERR(hp)) {
- destroy_irq(tile_hvc_irq);
+ irq_free_hwirq(tile_hvc_irq);
return PTR_ERR(hp);
}
dev_set_drvdata(&pdev->dev, hp);
@@ -155,7 +155,7 @@ static int hvc_tile_remove(struct platform_device *pdev)
rc = hvc_remove(hp);
if (rc == 0)
- destroy_irq(hp->data);
+ irq_free_hwirq(hp->data);
return rc;
}
diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c
index f92d7e6..613ccf0 100644
--- a/drivers/tty/serial/tilegx.c
+++ b/drivers/tty/serial/tilegx.c
@@ -359,8 +359,8 @@ static int tilegx_startup(struct uart_port *port)
}
/* Create our IRQs. */
- port->irq = create_irq();
- if (port->irq < 0)
+ port->irq = irq_alloc_hwirq(-1);
+ if (!port->irq)
goto err_uart_dest;
tile_irq_activate(port->irq, TILE_IRQ_PERCPU);
@@ -395,7 +395,7 @@ static int tilegx_startup(struct uart_port *port)
err_free_irq:
free_irq(port->irq, port);
err_dest_irq:
- destroy_irq(port->irq);
+ irq_free_hwirq(port->irq);
err_uart_dest:
gxio_uart_destroy(context);
ret = -ENXIO;
@@ -435,7 +435,7 @@ static void tilegx_shutdown(struct uart_port *port)
if (port->irq > 0) {
free_irq(port->irq, port);
- destroy_irq(port->irq);
+ irq_free_hwirq(port->irq);
port->irq = 0;
}
diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c
index f3713d3..0d24767 100644
--- a/drivers/usb/host/ehci-tilegx.c
+++ b/drivers/usb/host/ehci-tilegx.c
@@ -142,8 +142,8 @@ static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev)
ehci->hcs_params = readl(&ehci->caps->hcs_params);
/* Create our IRQs and register them. */
- pdata->irq = create_irq();
- if (pdata->irq < 0) {
+ pdata->irq = irq_alloc_hwirq(-1);
+ if (!pdata->irq) {
ret = -ENXIO;
goto err_no_irq;
}
@@ -175,7 +175,7 @@ static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev)
}
err_have_irq:
- destroy_irq(pdata->irq);
+ irq_free_hwirq(pdata->irq);
err_no_irq:
tilegx_stop_ehc();
usb_put_hcd(hcd);
@@ -193,7 +193,7 @@ static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
tilegx_stop_ehc();
gxio_usb_host_destroy(&pdata->usb_ctx);
- destroy_irq(pdata->irq);
+ irq_free_hwirq(pdata->irq);
return 0;
}
diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c
index 0b183e0..bef6dfb 100644
--- a/drivers/usb/host/ohci-tilegx.c
+++ b/drivers/usb/host/ohci-tilegx.c
@@ -129,8 +129,8 @@ static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev)
tilegx_start_ohc();
/* Create our IRQs and register them. */
- pdata->irq = create_irq();
- if (pdata->irq < 0) {
+ pdata->irq = irq_alloc_hwirq(-1);
+ if (!pdata->irq) {
ret = -ENXIO;
goto err_no_irq;
}
@@ -164,7 +164,7 @@ static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev)
}
err_have_irq:
- destroy_irq(pdata->irq);
+ irq_free_hwirq(pdata->irq);
err_no_irq:
tilegx_stop_ohc();
usb_put_hcd(hcd);
@@ -182,7 +182,7 @@ static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
tilegx_stop_ohc();
gxio_usb_host_destroy(&pdata->usb_ctx);
- destroy_irq(pdata->irq);
+ irq_free_hwirq(pdata->irq);
return 0;
}
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index dfa12a4..c919d3d 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -390,22 +390,7 @@ static void xen_irq_init(unsigned irq)
static int __must_check xen_allocate_irqs_dynamic(int nvec)
{
- int first = 0;
- int i, irq;
-
-#ifdef CONFIG_X86_IO_APIC
- /*
- * For an HVM guest or domain 0 which see "real" (emulated or
- * actual respectively) GSIs we allocate dynamic IRQs
- * e.g. those corresponding to event channels or MSIs
- * etc. from the range above those "real" GSIs to avoid
- * collisions.
- */
- if (xen_initial_domain() || xen_hvm_domain())
- first = get_nr_irqs_gsi();
-#endif
-
- irq = irq_alloc_descs_from(first, nvec, -1);
+ int i, irq = irq_alloc_descs(-1, 0, nvec, -1);
if (irq >= 0) {
for (i = 0; i < nvec; i++)
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index cb19f09..698ad05 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -199,6 +199,26 @@ extern int check_wakeup_irqs(void);
static inline int check_wakeup_irqs(void) { return 0; }
#endif
+/**
+ * struct irq_affinity_notify - context for notification of IRQ affinity changes
+ * @irq: Interrupt to which notification applies
+ * @kref: Reference count, for internal use
+ * @work: Work item, for internal use
+ * @notify: Function to be called on change. This will be
+ * called in process context.
+ * @release: Function to be called on release. This will be
+ * called in process context. Once registered, the
+ * structure must only be freed when this function is
+ * called or later.
+ */
+struct irq_affinity_notify {
+ unsigned int irq;
+ struct kref kref;
+ struct work_struct work;
+ void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
+ void (*release)(struct kref *ref);
+};
+
#if defined(CONFIG_SMP)
extern cpumask_var_t irq_default_affinity;
@@ -242,26 +262,6 @@ extern int irq_select_affinity(unsigned int irq);
extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);
-/**
- * struct irq_affinity_notify - context for notification of IRQ affinity changes
- * @irq: Interrupt to which notification applies
- * @kref: Reference count, for internal use
- * @work: Work item, for internal use
- * @notify: Function to be called on change. This will be
- * called in process context.
- * @release: Function to be called on release. This will be
- * called in process context. Once registered, the
- * structure must only be freed when this function is
- * called or later.
- */
-struct irq_affinity_notify {
- unsigned int irq;
- struct kref kref;
- struct work_struct work;
- void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
- void (*release)(struct kref *ref);
-};
-
extern int
irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
@@ -289,6 +289,12 @@ static inline int irq_set_affinity_hint(unsigned int irq,
{
return -EINVAL;
}
+
+static inline int
+irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
+{
+ return 0;
+}
#endif /* CONFIG_SMP */
/*
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 5c57efb..0d998d8 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -525,24 +525,6 @@ static inline void irq_set_percpu_devid_flags(unsigned int irq)
IRQ_NOPROBE | IRQ_PER_CPU_DEVID);
}
-/* Handle dynamic irq creation and destruction */
-extern unsigned int create_irq_nr(unsigned int irq_want, int node);
-extern unsigned int __create_irqs(unsigned int from, unsigned int count,
- int node);
-extern int create_irq(void);
-extern void destroy_irq(unsigned int irq);
-extern void destroy_irqs(unsigned int irq, unsigned int count);
-
-/*
- * Dynamic irq helper functions. Obsolete. Use irq_alloc_desc* and
- * irq_free_desc instead.
- */
-extern void dynamic_irq_cleanup(unsigned int irq);
-static inline void dynamic_irq_init(unsigned int irq)
-{
- dynamic_irq_cleanup(irq);
-}
-
/* Set/get chip/data for an IRQ: */
extern int irq_set_chip(unsigned int irq, struct irq_chip *chip);
extern int irq_set_handler_data(unsigned int irq, void *data);
@@ -625,17 +607,29 @@ int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
irq_alloc_descs(-1, from, cnt, node)
void irq_free_descs(unsigned int irq, unsigned int cnt);
-int irq_reserve_irqs(unsigned int from, unsigned int cnt);
-
static inline void irq_free_desc(unsigned int irq)
{
irq_free_descs(irq, 1);
}
-static inline int irq_reserve_irq(unsigned int irq)
+#ifdef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+unsigned int irq_alloc_hwirqs(int cnt, int node);
+static inline unsigned int irq_alloc_hwirq(int node)
+{
+ return irq_alloc_hwirqs(1, node);
+}
+void irq_free_hwirqs(unsigned int from, int cnt);
+static inline void irq_free_hwirq(unsigned int irq)
{
- return irq_reserve_irqs(irq, 1);
+ return irq_free_hwirqs(irq, 1);
}
+int arch_setup_hwirq(unsigned int irq, int node);
+void arch_teardown_hwirq(unsigned int irq);
+#endif
+
+#ifdef CONFIG_GENERIC_IRQ_LEGACY
+void irq_init_desc(unsigned int irq);
+#endif
#ifndef irq_reg_writel
# define irq_reg_writel(val, addr) writel(val, addr)
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 7ed92d0..45e2d8c 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -21,6 +21,8 @@
#define GIC_CPU_ACTIVEPRIO 0xd0
#define GIC_CPU_IDENT 0xfc
+#define GICC_IAR_INT_ID_MASK 0x3ff
+
#define GIC_DIST_CTRL 0x000
#define GIC_DIST_CTR 0x004
#define GIC_DIST_IGROUP 0x080
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 26e2661..472c021 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -27,6 +27,8 @@ struct irq_desc;
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
+ * @threads_handled: stats field for deferred spurious detection of threaded handlers
+ * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
@@ -52,6 +54,8 @@ struct irq_desc {
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
+ atomic_t threads_handled;
+ int threads_handled_last;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 07cbdfe..d269cec 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -5,6 +5,10 @@ menu "IRQ subsystem"
config MAY_HAVE_SPARSE_IRQ
bool
+# Legacy support, required for itanic
+config GENERIC_IRQ_LEGACY
+ bool
+
# Enable the generic irq autoprobe mechanism
config GENERIC_IRQ_PROBE
bool
@@ -17,6 +21,11 @@ config GENERIC_IRQ_SHOW
config GENERIC_IRQ_SHOW_LEVEL
bool
+# Facility to allocate a hardware interrupt. This is legacy support
+# and should not be used in new code. Use irq domains instead.
+config GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+ bool
+
# Support for delayed migration from interrupt context
config GENERIC_PENDING_IRQ
bool
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6397df2..a2b28a2 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -40,10 +40,9 @@ int irq_set_chip(unsigned int irq, struct irq_chip *chip)
irq_put_desc_unlock(desc, flags);
/*
* For !CONFIG_SPARSE_IRQ make the irq show up in
- * allocated_irqs. For the CONFIG_SPARSE_IRQ case, it is
- * already marked, and this call is harmless.
+ * allocated_irqs.
*/
- irq_reserve_irq(irq);
+ irq_mark_irq(irq);
return 0;
}
EXPORT_SYMBOL(irq_set_chip);
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index ddf1ffe..099ea2e 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -33,7 +33,7 @@ enum {
};
/*
- * Bit masks for desc->state
+ * Bit masks for desc->core_internal_state__do_not_mess_with_it
*
* IRQS_AUTODETECT - autodetection in progress
* IRQS_SPURIOUS_DISABLED - was disabled due to spurious interrupt
@@ -76,6 +76,12 @@ extern void mask_irq(struct irq_desc *desc);
extern void unmask_irq(struct irq_desc *desc);
extern void unmask_threaded_irq(struct irq_desc *desc);
+#ifdef CONFIG_SPARSE_IRQ
+static inline void irq_mark_irq(unsigned int irq) { }
+#else
+extern void irq_mark_irq(unsigned int irq);
+#endif
+
extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action);
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index bb07f29..7339e42 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -278,7 +278,12 @@ EXPORT_SYMBOL(irq_to_desc);
static void free_desc(unsigned int irq)
{
- dynamic_irq_cleanup(irq);
+ struct irq_desc *desc = irq_to_desc(irq);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+ desc_set_defaults(irq, desc, desc_node(desc), NULL);
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
}
static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
@@ -299,6 +304,20 @@ static int irq_expand_nr_irqs(unsigned int nr)
return -ENOMEM;
}
+void irq_mark_irq(unsigned int irq)
+{
+ mutex_lock(&sparse_irq_lock);
+ bitmap_set(allocated_irqs, irq, 1);
+ mutex_unlock(&sparse_irq_lock);
+}
+
+#ifdef CONFIG_GENERIC_IRQ_LEGACY
+void irq_init_desc(unsigned int irq)
+{
+ free_desc(irq);
+}
+#endif
+
#endif /* !CONFIG_SPARSE_IRQ */
/**
@@ -396,30 +415,56 @@ err:
}
EXPORT_SYMBOL_GPL(__irq_alloc_descs);
+#ifdef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
/**
- * irq_reserve_irqs - mark irqs allocated
- * @from: mark from irq number
- * @cnt: number of irqs to mark
+ * irq_alloc_hwirqs - Allocate an irq descriptor and initialize the hardware
+ * @cnt: number of interrupts to allocate
+ * @node: node on which to allocate
*
- * Returns 0 on success or an appropriate error code
+ * Returns an interrupt number > 0 or 0, if the allocation fails.
*/
-int irq_reserve_irqs(unsigned int from, unsigned int cnt)
+unsigned int irq_alloc_hwirqs(int cnt, int node)
{
- unsigned int start;
- int ret = 0;
+ int i, irq = __irq_alloc_descs(-1, 0, cnt, node, NULL);
- if (!cnt || (from + cnt) > nr_irqs)
- return -EINVAL;
+ if (irq < 0)
+ return 0;
- mutex_lock(&sparse_irq_lock);
- start = bitmap_find_next_zero_area(allocated_irqs, nr_irqs, from, cnt, 0);
- if (start == from)
- bitmap_set(allocated_irqs, start, cnt);
- else
- ret = -EEXIST;
- mutex_unlock(&sparse_irq_lock);
- return ret;
+ for (i = irq; cnt > 0; i++, cnt--) {
+ if (arch_setup_hwirq(i, node))
+ goto err;
+ irq_clear_status_flags(i, _IRQ_NOREQUEST);
+ }
+ return irq;
+
+err:
+ for (i--; i >= irq; i--) {
+ irq_set_status_flags(i, _IRQ_NOREQUEST | _IRQ_NOPROBE);
+ arch_teardown_hwirq(i);
+ }
+ irq_free_descs(irq, cnt);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irq_alloc_hwirqs);
+
+/**
+ * irq_free_hwirqs - Free irq descriptor and cleanup the hardware
+ * @from: Free from irq number
+ * @cnt: number of interrupts to free
+ *
+ */
+void irq_free_hwirqs(unsigned int from, int cnt)
+{
+ int i;
+
+ for (i = from; cnt > 0; i++, cnt--) {
+ irq_set_status_flags(i, _IRQ_NOREQUEST | _IRQ_NOPROBE);
+ arch_teardown_hwirq(i);
+ }
+ irq_free_descs(from, cnt);
}
+EXPORT_SYMBOL_GPL(irq_free_hwirqs);
+#endif
/**
* irq_get_next_irq - get next allocated irq number
@@ -482,20 +527,6 @@ int irq_set_percpu_devid(unsigned int irq)
return 0;
}
-/**
- * dynamic_irq_cleanup - cleanup a dynamically allocated irq
- * @irq: irq number to initialize
- */
-void dynamic_irq_cleanup(unsigned int irq)
-{
- struct irq_desc *desc = irq_to_desc(irq);
- unsigned long flags;
-
- raw_spin_lock_irqsave(&desc->lock, flags);
- desc_set_defaults(irq, desc, desc_node(desc), NULL);
- raw_spin_unlock_irqrestore(&desc->lock, flags);
-}
-
void kstat_incr_irq_this_cpu(unsigned int irq)
{
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index f140337..eb5e10e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -27,14 +27,14 @@ static struct irq_domain *irq_default_domain;
* __irq_domain_add() - Allocate a new irq_domain data structure
* @of_node: optional device-tree node of the interrupt controller
* @size: Size of linear map; 0 for radix mapping only
+ * @hwirq_max: Maximum number of interrupts supported by controller
* @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
* direct mapping
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
- * Allocates and initialize and irq_domain structure. Caller is expected to
- * register allocated irq_domain with irq_domain_register(). Returns pointer
- * to IRQ domain, or NULL on failure.
+ * Allocates and initialize and irq_domain structure.
+ * Returns pointer to IRQ domain, or NULL on failure.
*/
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
irq_hw_number_t hwirq_max, int direct_max,
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index d34131c..3dc6a61 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -886,8 +886,8 @@ static int irq_thread(void *data)
irq_thread_check_affinity(desc, action);
action_ret = handler_fn(desc, action);
- if (!noirqdebug)
- note_interrupt(action->irq, desc, action_ret);
+ if (action_ret == IRQ_HANDLED)
+ atomic_inc(&desc->threads_handled);
wake_threads_waitq(desc);
}
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c
index a1d8cc6..e2514b0 100644
--- a/kernel/irq/spurious.c
+++ b/kernel/irq/spurious.c
@@ -270,6 +270,8 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
return action && (action->flags & IRQF_IRQPOLL);
}
+#define SPURIOUS_DEFERRED 0x80000000
+
void note_interrupt(unsigned int irq, struct irq_desc *desc,
irqreturn_t action_ret)
{
@@ -277,15 +279,111 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc,
irq_settings_is_polled(desc))
return;
- /* we get here again via the threaded handler */
- if (action_ret == IRQ_WAKE_THREAD)
- return;
-
if (bad_action_ret(action_ret)) {
report_bad_irq(irq, desc, action_ret);
return;
}
+ /*
+ * We cannot call note_interrupt from the threaded handler
+ * because we need to look at the compound of all handlers
+ * (primary and threaded). Aside of that in the threaded
+ * shared case we have no serialization against an incoming
+ * hardware interrupt while we are dealing with a threaded
+ * result.
+ *
+ * So in case a thread is woken, we just note the fact and
+ * defer the analysis to the next hardware interrupt.
+ *
+ * The threaded handlers store whether they sucessfully
+ * handled an interrupt and we check whether that number
+ * changed versus the last invocation.
+ *
+ * We could handle all interrupts with the delayed by one
+ * mechanism, but for the non forced threaded case we'd just
+ * add pointless overhead to the straight hardirq interrupts
+ * for the sake of a few lines less code.
+ */
+ if (action_ret & IRQ_WAKE_THREAD) {
+ /*
+ * There is a thread woken. Check whether one of the
+ * shared primary handlers returned IRQ_HANDLED. If
+ * not we defer the spurious detection to the next
+ * interrupt.
+ */
+ if (action_ret == IRQ_WAKE_THREAD) {
+ int handled;
+ /*
+ * We use bit 31 of thread_handled_last to
+ * denote the deferred spurious detection
+ * active. No locking necessary as
+ * thread_handled_last is only accessed here
+ * and we have the guarantee that hard
+ * interrupts are not reentrant.
+ */
+ if (!(desc->threads_handled_last & SPURIOUS_DEFERRED)) {
+ desc->threads_handled_last |= SPURIOUS_DEFERRED;
+ return;
+ }
+ /*
+ * Check whether one of the threaded handlers
+ * returned IRQ_HANDLED since the last
+ * interrupt happened.
+ *
+ * For simplicity we just set bit 31, as it is
+ * set in threads_handled_last as well. So we
+ * avoid extra masking. And we really do not
+ * care about the high bits of the handled
+ * count. We just care about the count being
+ * different than the one we saw before.
+ */
+ handled = atomic_read(&desc->threads_handled);
+ handled |= SPURIOUS_DEFERRED;
+ if (handled != desc->threads_handled_last) {
+ action_ret = IRQ_HANDLED;
+ /*
+ * Note: We keep the SPURIOUS_DEFERRED
+ * bit set. We are handling the
+ * previous invocation right now.
+ * Keep it for the current one, so the
+ * next hardware interrupt will
+ * account for it.
+ */
+ desc->threads_handled_last = handled;
+ } else {
+ /*
+ * None of the threaded handlers felt
+ * responsible for the last interrupt
+ *
+ * We keep the SPURIOUS_DEFERRED bit
+ * set in threads_handled_last as we
+ * need to account for the current
+ * interrupt as well.
+ */
+ action_ret = IRQ_NONE;
+ }
+ } else {
+ /*
+ * One of the primary handlers returned
+ * IRQ_HANDLED. So we don't care about the
+ * threaded handlers on the same line. Clear
+ * the deferred detection bit.
+ *
+ * In theory we could/should check whether the
+ * deferred bit is set and take the result of
+ * the previous run into account here as
+ * well. But it's really not worth the
+ * trouble. If every other interrupt is
+ * handled we never trigger the spurious
+ * detector. And if this is just the one out
+ * of 100k unhandled ones which is handled
+ * then we merily delay the spurious detection
+ * by one hard interrupt. Not a real problem.
+ */
+ desc->threads_handled_last &= ~SPURIOUS_DEFERRED;
+ }
+ }
+
if (unlikely(action_ret == IRQ_NONE)) {
/*
* If we are seeing only the odd spurious IRQ caused by