]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 22:59:13 +0000 (15:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 22:59:13 +0000 (15:59 -0700)
Pull core irq updates from Thomas Gleixner:
 "The irq department delivers:

   - Another tree wide update to get rid of the horrible create_irq
     interface along with its even more horrible variants.  That also
     gets rid of the last leftovers of the initial sparse irq hackery.
     arch/driver specific changes have been either acked or ignored.

   - A fix for the spurious interrupt detection logic with threaded
     interrupts.

   - A new ARM SoC interrupt controller

   - The usual pile of fixes and improvements all over the place"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (40 commits)
  Documentation: brcmstb-l2: Add Broadcom STB Level-2 interrupt controller binding
  irqchip: brcmstb-l2: Add Broadcom Set Top Box Level-2 interrupt controller
  genirq: Improve documentation to match current implementation
  ARM: iop13xx: fix msi support with sparse IRQ
  genirq: Provide !SMP stub for irq_set_affinity_notifier()
  irqchip: armada-370-xp: Move the devicetree binding documentation
  irqchip: gic: Use mask field in GICC_IAR
  genirq: Remove dynamic_irq mess
  ia64: Use irq_init_desc
  genirq: Replace dynamic_irq_init/cleanup
  genirq: Remove irq_reserve_irq[s]
  genirq: Replace reserve_irqs in core code
  s390: Avoid call to irq_reserve_irqs()
  s390: Remove pointless arch_show_interrupts()
  s390: pci: Check return value of alloc_irq_desc() proper
  sh: intc: Remove pointless irq_reserve_irqs() invocation
  x86, irq: Remove pointless irq_reserve_irqs() call
  genirq: Make create/destroy_irq() ia64 private
  tile: Use SPARSE_IRQ
  tile: pci: Use irq_alloc/free_hwirq()
  ...

56 files changed:
Documentation/IRQ-domain.txt
Documentation/devicetree/bindings/interrupt-controller/brcm,l2-intc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt [moved from Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt with 100% similarity]
arch/arm/Kconfig
arch/arm/mach-iop13xx/include/mach/irqs.h
arch/arm/mach-iop13xx/include/mach/time.h
arch/arm/mach-iop13xx/iq81340mc.c
arch/arm/mach-iop13xx/iq81340sc.c
arch/arm/mach-iop13xx/msi.c
arch/arm/mach-iop13xx/setup.c
arch/arm/mach-iop13xx/tpmi.c
arch/ia64/Kconfig
arch/ia64/include/asm/hw_irq.h
arch/ia64/include/asm/irq.h
arch/ia64/include/asm/irq_remapping.h
arch/ia64/kernel/iosapic.c
arch/ia64/kernel/irq_ia64.c
arch/mips/pci/msi-xlp.c
arch/mips/pci/pci-xlr.c
arch/s390/kernel/irq.c
arch/s390/pci/pci.c
arch/tile/Kconfig
arch/tile/include/asm/irq.h
arch/tile/kernel/irq.c
arch/tile/kernel/pci_gx.c
arch/x86/Kconfig
arch/x86/include/asm/io_apic.h
arch/x86/include/asm/irq_remapping.h
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/hpet.c
arch/x86/platform/uv/uv_irq.c
drivers/iommu/dmar.c
drivers/iommu/irq_remapping.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-brcmstb-l2.c [new file with mode: 0644]
drivers/irqchip/irq-gic.c
drivers/net/ethernet/tile/tilegx.c
drivers/pci/htirq.c
drivers/sh/intc/core.c
drivers/tty/hvc/hvc_tile.c
drivers/tty/serial/tilegx.c
drivers/usb/host/ehci-tilegx.c
drivers/usb/host/ohci-tilegx.c
drivers/xen/events/events_base.c
include/linux/interrupt.h
include/linux/irq.h
include/linux/irqchip/arm-gic.h
include/linux/irqdesc.h
kernel/irq/Kconfig
kernel/irq/chip.c
kernel/irq/internals.h
kernel/irq/irqdesc.c
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/spurious.c

index 03df71aeb38c000dd607b657f62ebb477753bf76..8a8b82c9ca5367b8d5859b221ca94c643ee3c978 100644 (file)
@@ -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 (file)
index 0000000..448273a
--- /dev/null
@@ -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>;
+};
index 071dce78959a466c5c7c68e5c803884ecab8102c..ad89a033f17f84e3664399a6d0146f720cac87dc 100644 (file)
@@ -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.
 
index 054e7acb5bfa36264c64eecaf950c4480c769fe0..e8d24d32121a1811b2ddaa580ce7ff435692caee 100644 (file)
@@ -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_ */
index f1c00d6d560ba497a8fc9ffaf47913f513d8036e..15bc9bb78a6b616f7361e9f5c9cbed255bb03f87 100644 (file)
@@ -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
index 02a8228ac2d3d4d3b6217fb8652120f16d1e018c..9cd07d39609383a67ce4422dd881a72d3ef5c280 100644 (file)
@@ -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
index 1b80f10722b3fdb01f5e8aea44eac8b60113cb4e..b3ec11cb707e67f84592d5846dbfd37feeaac959 100644 (file)
@@ -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
index 560d5b2dec2270c00e06bebbe8bac54d7caa8585..e7730cf9c15d27d7786a2ce441ad7ed6ed829c4c 100644 (file)
 #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);
+}
index 96e6c7a6793becb69e98b9f31211f8ab6e54b56d..bca96f4334953fd7a4ec8b5f155358c0b5e82fea 100644 (file)
@@ -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
index 6fdad7a0425af0e1a6565b1fc3a45aab5f27aa35..db511ec2b1df6824cb6d3d24659cfebe2428d5ec 100644 (file)
@@ -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))
index 12c3afee0f6fabcd664c12e7ddca1bf367bc401e..2f3abcf8f6bc9b2b206652b52cf1318f93acb74a 100644 (file)
@@ -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
index a681d02cb324c02a4d1f39cf0d5b1b301901d271..029bab36cd91c0e5949a7ad04ad68264a4be81ad 100644 (file)
@@ -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))
index 91b920fd7d534099073c1ca44c69938cc445c9d4..820667cbea7e337b3c2b655c30501418e7670b9e 100644 (file)
@@ -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 */
index a8687b1d8906912ab458b9640db72c8655115a3f..e3b3556e2e1bb8d32b67a1b63e4de3e2708af0b8 100644 (file)
@@ -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
index 19f107be734e6e9db90c050b2389eb2a79cc1b71..cd44a57c73be76b48227c285b4cf0421f5efdc1f 100644 (file)
@@ -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;
index 0884f5ecbcc3e97b86a5e4c248614359dbd7674c..03ea78ed64a93384f8d4730720b99fa34e02b2d1 100644 (file)
@@ -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);
 }
 
index afd8405e0188248dbea2dd90e2e8949e25895d67..3249685e03ad6f2771c4da88e59d62697ef0c855 100644 (file)
@@ -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;
index 4427abbd48b547a606fbe47a6d6bf7e5497b2bd7..0dde80332d3a14c18594b673e6aca15f67b4a9b5 100644 (file)
@@ -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;
index c7463aa0014b5a499d254028c77770f4ec4c7f07..99b0b09646caac5153cc51301fd9a5e2aa93b315 100644 (file)
@@ -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;
 }
 
 /*
index bdf02570d1dfab0c81146b4b609cbe45c9b445d7..9ddc51eeb8d690410aa6fbf98e1a36a6d544e4db 100644 (file)
@@ -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)
index 85258ca43ff5be5411c7d6d0c847cb030c9848ed..4f3006b600e32f2af308dd1aaffe22a35db5d418 100644 (file)
@@ -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
index 33cff9a3058b02ebb2104ab04bcc85741ec83e0d..1fe86911838b272d80fc0c7232a9f56b21f0e949 100644 (file)
 #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)
 
index 906a76bdb31d488e6ffa7fd4416e091812d611fc..637f2ffaa5f56159bb7eee1fe0e8b2eda8f97579 100644 (file)
@@ -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
index 077b7bc437e5f6a54e5ad28e42e89e524001dbf0..e39f9c542807c0cae9e329c4641e6c9429c263f1 100644 (file)
@@ -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);
 }
index 7d5feb5908ddeabf3fcab0a3114e2c318c0537d3..7a01d4335029b1f49f23c22e709efe20542e7356 100644 (file)
@@ -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"
index 459e50a424d148eb94163a343cbf17a8b4abfad9..90f97b4b93476e319438ba93d2c72e2f8b1adc24 100644 (file)
@@ -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);
 
index d806b228d2c0d606942409c4d3fac8eed5dc1265..b7747c4c2cf2ff0e73e2e0896fe550825b827eba 100644 (file)
@@ -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 */
index 992060e09897dd2b068f8d8ebf6f02fa15fa2845..9d0a9795a0f80e56e7ff9acfd414ee7f5ed44459 100644 (file)
@@ -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;
index 4177bfbc80b0d44400c563fa46affdbe79bc25d9..5f5a147d1cd2559cfc0c0053a0e35b31fc5a5ccd 100644 (file)
@@ -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;
 
index acf7752da952f52cc43b56f5133f4d8fe97e56e4..b233681af4de6ecaaad9305546496b3ea83334b8 100644 (file)
@@ -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);
index 39f8b717fe8482f6e757bc7f9064187923b90cca..9a4f05e5b23fa480e308d0b44373e6599ad82982 100644 (file)
@@ -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;
        }
 
index 228632c99adbae9080f9abea301c1c7335121f48..33c43952408067f604c5fa8a6b0d707086408095 100644 (file)
@@ -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;
 }
 
index d770f7406631298d2e400b9b1adee31adce76bad..bbb746e355002b56544f135641d52bfe19171c79 100644 (file)
@@ -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
index f180f8d5fb7b69011cb9860298e0422602ac3a53..62a13e5ef98f0c47c585d6dd0d5b59dcfae5b189 100644 (file)
@@ -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 (file)
index 0000000..8ee2a36
--- /dev/null
@@ -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);
index 57d165e026f43ac4ba3f0ba0a0f422025ad2b117..7e11c9d6ae8c8411610987339dc161f208cf2ad2 100644 (file)
@@ -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);
index 7e1c91d41a87ff2a4e065718d4caaf84c7db8020..449011b0e007d6b798a91728eaa845e05e581305 100644 (file)
@@ -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;
index 6e373ea57b32834df2076b26b4f0d72941933644..d68b030ab5338b2f945a0dce447b8641eeb629ac 100644 (file)
@@ -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);
 }
index 8f32a1323a79dd40e6e91433fa38e15709a3ca02..81f22980b2def237fbefb4feac8386194188653e 100644 (file)
@@ -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);
index af8cdaa1dcb90b50d9ee1143e933d51d7fc6696d..147d49e95db2b1a61437581aec0c1bbb6cbf35c6 100644 (file)
@@ -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;
 }
index f92d7e6bd8765366d924d2abbd7d57279ff530ab..613ccf09dc2a224e03e55ea951ed06d8d2bc31b0 100644 (file)
@@ -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;
        }
 
index f3713d32c9a157ab3f5414cb3350ca8752816c1a..0d247673c3ca29e604ca1f3ea58111359925f319 100644 (file)
@@ -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;
 }
index 0b183e0b0a8a1283a25ffea553a0e94cffcbd3ee..bef6dfb0405a0947433ec148457f7c7527d1cccb 100644 (file)
@@ -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;
 }
index dfa12a4a0a48ffbaea7f64e4b0c95e6b2ca38932..c919d3d5c8458742aabb2963624f541004e8df18 100644 (file)
@@ -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++)
index cb19f09d7e3e7b72e552d142f38f326511e56eee..698ad053d064aef74793449f4b4b55018994a908 100644 (file)
@@ -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 */
 
 /*
index 5c57efb863d08e5937a36e06778efa8047060156..0d998d8b01d8a26e9d27e02bb9c2ace71b21ea85 100644 (file)
@@ -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)
index 7ed92d0560d59de9f4db4eab3d9940312807c417..45e2d8c15bd211d0fa15473efc4e1f3f8c6f17e3 100644 (file)
@@ -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
index 26e2661d3935693105d9bcffef8381e1e32f3677..472c021a2d4f09923cd3913647be44c016ff3003 100644 (file)
@@ -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
index 07cbdfea9ae26a6584dee41cbef104c7b05ef111..d269cecdfbf08a48c3a24b1adf89a037eff7485d 100644 (file)
@@ -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
index 6397df2d6945b09ae853c1a75e4d18aa6da7096f..a2b28a2fd7b16d270c45b3c40719a35d0034c58d 100644 (file)
@@ -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);
index ddf1ffeb79f1e3ac5a5408465d4f31391525de13..099ea2e0eb8833676b3d234f2740487873bac093 100644 (file)
@@ -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);
index bb07f2928f4b9c2ca33803f712c8889ca5823907..7339e42a85ab9bb7f425a4da7806a0a37d9ec36c 100644 (file)
@@ -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));
index f14033700c25cbc6872ccc2e60d33ee25a3493e9..eb5e10e32e0549bec6f2dd08136f6934bb1c1b47 100644 (file)
@@ -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,
index d34131ca372baee79aa17ca2670cde5ff32a2cc8..3dc6a61bf06a447acb70f67fc3942fc2286e32d2 100644 (file)
@@ -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);
        }
index a1d8cc63b56e55ddbb83741b1d20db58e5ab3b7c..e2514b0e439e942e19245558e0fad10918f84371 100644 (file)
@@ -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