Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next

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()
  ...
This commit is contained in:
Linus Torvalds 2014-06-04 15:59:13 -07:00
commit d09cc3659d
56 changed files with 597 additions and 396 deletions

View 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

View File

@ -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>;
};

View 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.

View 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_ */

View 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

View 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

View 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

View File

@ -23,10 +23,7 @@
#include <linux/msi.h>
#include <asm/mach/irq.h>
#include <asm/irq.h>
#define IOP13XX_NUM_MSI_IRQS 128
static DECLARE_BITMAP(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
#include <mach/irqs.h>
/* IMIPR0 CP6 R8 Page 1
*/
@ -121,41 +118,6 @@ void __init iop13xx_msi_init(void)
irq_set_chained_handler(IRQ_IOP13XX_INBD_MSI, iop13xx_msi_handler);
}
/*
* Dynamic irq allocate and deallocation
*/
int create_irq(void)
{
int irq, pos;
again:
pos = find_first_zero_bit(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
irq = IRQ_IOP13XX_MSI_0 + pos;
if (irq > NR_IRQS)
return -ENOSPC;
/* test_and_set_bit operates on 32-bits at a time */
if (test_and_set_bit(pos, msi_irq_in_use))
goto again;
dynamic_irq_init(irq);
return irq;
}
void destroy_irq(unsigned int irq)
{
int pos = irq - IRQ_IOP13XX_MSI_0;
dynamic_irq_cleanup(irq);
clear_bit(pos, msi_irq_in_use);
}
void arch_teardown_msi_irq(unsigned int irq)
{
destroy_irq(irq);
}
static void iop13xx_msi_nop(struct irq_data *d)
{
return;
@ -172,12 +134,17 @@ static struct irq_chip iop13xx_msi_chip = {
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
int id, irq = create_irq();
int id, irq = irq_alloc_desc_from(IRQ_IOP13XX_MSI_0, -1);
struct msi_msg msg;
if (irq < 0)
return irq;
if (irq >= NR_IOP13XX_IRQS) {
irq_free_desc(irq);
return -ENOSPC;
}
irq_set_msi_desc(irq, desc);
msg.address_hi = 0x0;
@ -191,3 +158,8 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
return 0;
}
void arch_teardown_msi_irq(unsigned int irq)
{
irq_free_desc(irq);
}

View 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

View 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))

View 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

View 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))

View 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 */

View 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

View 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;

View 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);
}

View 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;

View 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;

View 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 @@ int show_interrupts(struct seq_file *p, void *v)
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;
}
/*

View 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)

View 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

View File

@ -18,10 +18,12 @@
#include <linux/hardirq.h>
/* The hypervisor interface provides 32 IRQs. */
#define NR_IRQS 32
#define NR_IRQS 32
/* IRQ numbers used for linux IPIs. */
#define IRQ_RESCHEDULE 0
#define IRQ_RESCHEDULE 0
/* Interrupts for dynamic allocation start at 1. Let the core allocate irq0 */
#define NR_IRQS_LEGACY 1
#define irq_canonicalize(irq) (irq)

View 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

View 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 @@ int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
/* 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);
}

View 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"

View 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);

View 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 */

View 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;
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;

View 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;

View 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);

View 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;
}

View 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;
}

View 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

View 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

View File

@ -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);

View 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);

View 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;

View 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);
}

View 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);

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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++)

View 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 */
/*

View 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_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)

View 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

View 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

View 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

View 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);

View 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);

View 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 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
}
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));

View 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,

View 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);
}

View 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