diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt index 03df71aeb38c..8a8b82c9ca53 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 000000000000..448273a30a11 --- /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 similarity index 100% rename from Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt rename to Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 071dce78959a..ad89a033f17f 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 054e7acb5bfa..e8d24d32121a 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 f1c00d6d560b..15bc9bb78a6b 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 + #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 02a8228ac2d3..9cd07d396093 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 1b80f10722b3..b3ec11cb707e 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 560d5b2dec22..e7730cf9c15d 100644 --- a/arch/arm/mach-iop13xx/msi.c +++ b/arch/arm/mach-iop13xx/msi.c @@ -23,10 +23,7 @@ #include #include #include - - -#define IOP13XX_NUM_MSI_IRQS 128 -static DECLARE_BITMAP(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS); +#include /* 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 96e6c7a6793b..bca96f433495 100644 --- a/arch/arm/mach-iop13xx/setup.c +++ b/arch/arm/mach-iop13xx/setup.c @@ -27,6 +27,7 @@ #include #include #include +#include #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 6fdad7a0425a..db511ec2b1df 100644 --- a/arch/arm/mach-iop13xx/tpmi.c +++ b/arch/arm/mach-iop13xx/tpmi.c @@ -24,6 +24,7 @@ #include #include #include +#include /* 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 12c3afee0f6f..2f3abcf8f6bc 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 a681d02cb324..029bab36cd91 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 91b920fd7d53..820667cbea7e 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 a8687b1d8906..e3b3556e2e1b 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 19f107be734e..cd44a57c73be 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 0884f5ecbcc3..03ea78ed64a9 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 afd8405e0188..3249685e03ad 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 4427abbd48b5..0dde80332d3a 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 c7463aa0014b..99b0b09646ca 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 bdf02570d1df..9ddc51eeb8d6 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 85258ca43ff5..4f3006b600e3 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 33cff9a3058b..1fe86911838b 100644 --- a/arch/tile/include/asm/irq.h +++ b/arch/tile/include/asm/irq.h @@ -18,10 +18,12 @@ #include /* 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 906a76bdb31d..637f2ffaa5f5 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 077b7bc437e5..e39f9c542807 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 7d5feb5908dd..7a01d4335029 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 459e50a424d1..90f97b4b9347 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 d806b228d2c0..b7747c4c2cf2 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 992060e09897..9d0a9795a0f8 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; + int ret; - if (from < nr_irqs_gsi) - from = nr_irqs_gsi; - - 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; + ret = __assign_irq_vector(irq, cfg, apic->target_cpus()); 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]); - 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; + if (!ret) + irq_set_chip_data(irq, cfg); + else + free_irq_cfg(irq, cfg); + return ret; } -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; -} - -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 4177bfbc80b0..5f5a147d1cd2 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 acf7752da952..b233681af4de 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 39f8b717fe84..9a4f05e5b23f 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 228632c99adb..33c439524080 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 d770f7406631..bbb746e35500 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 f180f8d5fb7b..62a13e5ef98f 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 000000000000..8ee2a36d5840 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 57d165e026f4..7e11c9d6ae8c 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 7e1c91d41a87..449011b0e007 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 6e373ea57b32..d68b030ab533 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 8f32a1323a79..81f22980b2de 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 af8cdaa1dcb9..147d49e95db2 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 f92d7e6bd876..613ccf09dc2a 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 f3713d32c9a1..0d247673c3ca 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 0b183e0b0a8a..bef6dfb0405a 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 dfa12a4a0a48..c919d3d5c845 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 cb19f09d7e3e..698ad053d064 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 5c57efb863d0..0d998d8b01d8 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_reserve_irqs(irq, 1); + 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_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 7ed92d0560d5..45e2d8c15bd2 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 26e2661d3935..472c021a2d4f 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 07cbdfea9ae2..d269cecdfbf0 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 6397df2d6945..a2b28a2fd7b1 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 ddf1ffeb79f1..099ea2e0eb88 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 bb07f2928f4b..7339e42a85ab 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 f14033700c25..eb5e10e32e05 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 d34131ca372b..3dc6a61bf06a 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 a1d8cc63b56e..e2514b0e439e 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