mirror of https://gitee.com/openkylin/linux.git
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:
commit
d09cc3659d
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue