diff options
Diffstat (limited to 'drivers/irqchip')
| -rw-r--r-- | drivers/irqchip/Kconfig | 22 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 3 | ||||
| -rw-r--r-- | drivers/irqchip/exynos-combiner.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 95 | ||||
| -rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 202 | ||||
| -rw-r--r-- | drivers/irqchip/irq-clps711x.c | 243 | ||||
| -rw-r--r-- | drivers/irqchip/irq-crossbar.c | 208 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 103 | ||||
| -rw-r--r-- | drivers/irqchip/irq-mmp.c | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-mxs.c | 4 | ||||
| -rw-r--r-- | drivers/irqchip/irq-orion.c | 4 | ||||
| -rw-r--r-- | drivers/irqchip/irq-s3c24xx.c | 6 | ||||
| -rw-r--r-- | drivers/irqchip/irq-sirfsoc.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-vic.c | 66 | ||||
| -rw-r--r-- | drivers/irqchip/irqchip.c | 6 | ||||
| -rw-r--r-- | drivers/irqchip/irqchip.h | 7 | ||||
| -rw-r--r-- | drivers/irqchip/spear-shirq.c | 4 |
17 files changed, 899 insertions, 82 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 61ffdca96e2..bbb746e3550 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -30,6 +30,12 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config BRCMSTB_L2_IRQ + bool + depends on ARM + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config DW_APB_ICTL bool select IRQ_DOMAIN @@ -39,6 +45,14 @@ config IMGPDC_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config CLPS711X_IRQCHIP + bool + depends on ARCH_CLPS711X + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + select SPARSE_IRQ + default y + config ORION_IRQCHIP bool select IRQ_DOMAIN @@ -69,3 +83,11 @@ config VERSATILE_FPGA_IRQ_NR config XTENSA_MX bool select IRQ_DOMAIN + +config IRQ_CROSSBAR + bool + help + Support for a CROSSBAR ip that preceeds the main interrupt controller. + The primary irqchip invokes the crossbar's callback which inturn allocates + a free irq and configures the IP. Thus the peripheral interrupts are + routed to one of the free irqchip interrupt lines. diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 1c0c151d108..62a13e5ef98 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o +obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o @@ -27,3 +28,5 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o +obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o +obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 40e6440348f..f8636a650cf 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -17,7 +17,6 @@ #include <linux/irqchip/chained_irq.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <asm/mach/irq.h> #include "irqchip.h" @@ -81,7 +80,7 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq); if (unlikely(!cascade_irq)) - do_bad_IRQ(irq, desc); + handle_bad_irq(irq, desc); else generic_handle_irq(cascade_irq); diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 41be897df8d..574aba0eba4 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -19,6 +19,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/irqchip/chained_irq.h> +#include <linux/cpu.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -41,6 +42,7 @@ #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) +#define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) #define ARMADA_375_PPI_CAUSE (0x10) @@ -132,8 +134,7 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, struct msi_desc *desc) { struct msi_msg msg; - irq_hw_number_t hwirq; - int virq; + int virq, hwirq; hwirq = armada_370_xp_alloc_msi(); if (hwirq < 0) @@ -159,8 +160,19 @@ static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); + unsigned long hwirq = d->hwirq; + irq_dispose_mapping(irq); - armada_370_xp_free_msi(d->hwirq); + armada_370_xp_free_msi(hwirq); +} + +static int armada_370_xp_check_msi_device(struct msi_chip *chip, struct pci_dev *dev, + int nvec, int type) +{ + /* We support MSI, but not MSI-X */ + if (type == PCI_CAP_ID_MSI) + return 0; + return -EINVAL; } static struct irq_chip armada_370_xp_msi_irq_chip = { @@ -201,6 +213,7 @@ static int armada_370_xp_msi_init(struct device_node *node, msi_chip->setup_irq = armada_370_xp_setup_msi_irq; msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; + msi_chip->check_device = armada_370_xp_check_msi_device; msi_chip->of_node = node; armada_370_xp_msi_domain = @@ -244,35 +257,18 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); static int armada_xp_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { - unsigned long reg; - unsigned long new_mask = 0; - unsigned long online_mask = 0; - unsigned long count = 0; irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long reg, mask; int cpu; - for_each_cpu(cpu, mask_val) { - new_mask |= 1 << cpu_logical_map(cpu); - count++; - } - - /* - * Forbid mutlicore interrupt affinity - * This is required since the MPIC HW doesn't limit - * several CPUs from acknowledging the same interrupt. - */ - if (count > 1) - return -EINVAL; - - for_each_cpu(cpu, cpu_online_mask) - online_mask |= 1 << cpu_logical_map(cpu); + /* Select a single core from the affinity mask which is online */ + cpu = cpumask_any_and(mask_val, cpu_online_mask); + mask = 1UL << cpu_logical_map(cpu); raw_spin_lock(&irq_controller_lock); - reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - reg = (reg & (~online_mask)) | new_mask; + reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - raw_spin_unlock(&irq_controller_lock); return 0; @@ -315,7 +311,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, } #ifdef CONFIG_SMP -void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) +static void armada_mpic_send_doorbell(const struct cpumask *mask, + unsigned int irq) { int cpu; unsigned long map = 0; @@ -335,8 +332,17 @@ void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) ARMADA_370_XP_SW_TRIG_INT_OFFS); } -void armada_xp_mpic_smp_cpu_init(void) +static void armada_xp_mpic_smp_cpu_init(void) { + u32 control; + int nr_irqs, i; + + control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); + nr_irqs = (control >> 2) & 0x3ff; + + for (i = 0; i < nr_irqs; i++) + writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); + /* Clear pending IPIs */ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); @@ -347,6 +353,20 @@ void armada_xp_mpic_smp_cpu_init(void) /* Unmask IPI interrupt */ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } + +static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + armada_xp_mpic_smp_cpu_init(); + return NOTIFY_OK; +} + +static struct notifier_block armada_370_xp_mpic_cpu_notifier = { + .notifier_call = armada_xp_mpic_secondary_init, + .priority = 100, +}; + #endif /* CONFIG_SMP */ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -463,7 +483,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { struct resource main_int_res, per_cpu_int_res; - int parent_irq; + int parent_irq, nr_irqs, i; u32 control; BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -485,24 +505,19 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!per_cpu_int_base); control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); + nr_irqs = (control >> 2) & 0x3ff; + + for (i = 0; i < nr_irqs; i++) + writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); armada_370_xp_mpic_domain = - irq_domain_add_linear(node, (control >> 2) & 0x3ff, + irq_domain_add_linear(node, nr_irqs, &armada_370_xp_mpic_irq_ops, NULL); BUG_ON(!armada_370_xp_mpic_domain); #ifdef CONFIG_SMP armada_xp_mpic_smp_cpu_init(); - - /* - * Set the default affinity from all CPUs to the boot cpu. - * This is required since the MPIC doesn't limit several CPUs - * from acknowledging the same interrupt. - */ - cpumask_clear(irq_default_affinity); - cpumask_set_cpu(smp_processor_id(), irq_default_affinity); - #endif armada_370_xp_msi_init(node, main_int_res.start); @@ -511,6 +526,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, if (parent_irq <= 0) { irq_set_default_host(armada_370_xp_mpic_domain); set_handle_irq(armada_370_xp_handle_irq); +#ifdef CONFIG_SMP + set_smp_cross_call(armada_mpic_send_doorbell); + register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); +#endif } else { irq_set_chained_handler(parent_irq, armada_370_xp_mpic_handle_cascade_irq); diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c new file mode 100644 index 00000000000..c15c840987d --- /dev/null +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -0,0 +1,202 @@ +/* + * Generic Broadcom Set Top Box Level 2 Interrupt controller driver + * + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/irqdomain.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> + +#include <asm/mach/irq.h> + +#include "irqchip.h" + +/* Register offsets in the L2 interrupt controller */ +#define CPU_STATUS 0x00 +#define CPU_SET 0x04 +#define CPU_CLEAR 0x08 +#define CPU_MASK_STATUS 0x0c +#define CPU_MASK_SET 0x10 +#define CPU_MASK_CLEAR 0x14 + +/* L2 intc private data structure */ +struct brcmstb_l2_intc_data { + int parent_irq; + void __iomem *base; + struct irq_domain *domain; + bool can_wake; + u32 saved_mask; /* for suspend/resume */ +}; + +static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) +{ + struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 status; + + chained_irq_enter(chip, desc); + + status = __raw_readl(b->base + CPU_STATUS) & + ~(__raw_readl(b->base + CPU_MASK_STATUS)); + + if (status == 0) { + do_bad_IRQ(irq, desc); + goto out; + } + + do { + irq = ffs(status) - 1; + /* ack at our level */ + __raw_writel(1 << irq, b->base + CPU_CLEAR); + status &= ~(1 << irq); + generic_handle_irq(irq_find_mapping(b->domain, irq)); + } while (status); +out: + chained_irq_exit(chip, desc); +} + +static void brcmstb_l2_intc_suspend(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct brcmstb_l2_intc_data *b = gc->private; + + irq_gc_lock(gc); + /* Save the current mask */ + b->saved_mask = __raw_readl(b->base + CPU_MASK_STATUS); + + if (b->can_wake) { + /* Program the wakeup mask */ + __raw_writel(~gc->wake_active, b->base + CPU_MASK_SET); + __raw_writel(gc->wake_active, b->base + CPU_MASK_CLEAR); + } + irq_gc_unlock(gc); +} + +static void brcmstb_l2_intc_resume(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct brcmstb_l2_intc_data *b = gc->private; + + irq_gc_lock(gc); + /* Clear unmasked non-wakeup interrupts */ + __raw_writel(~b->saved_mask & ~gc->wake_active, b->base + CPU_CLEAR); + + /* Restore the saved mask */ + __raw_writel(b->saved_mask, b->base + CPU_MASK_SET); + __raw_writel(~b->saved_mask, b->base + CPU_MASK_CLEAR); + irq_gc_unlock(gc); +} + +int __init brcmstb_l2_intc_of_init(struct device_node *np, + struct device_node *parent) +{ + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct brcmstb_l2_intc_data *data; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->base = of_iomap(np, 0); + if (!data->base) { + pr_err("failed to remap intc L2 registers\n"); + ret = -ENOMEM; + goto out_free; + } + + /* Disable all interrupts by default */ + __raw_writel(0xffffffff, data->base + CPU_MASK_SET); + __raw_writel(0xffffffff, data->base + CPU_CLEAR); + + data->parent_irq = irq_of_parse_and_map(np, 0); + if (data->parent_irq < 0) { + pr_err("failed to find parent interrupt\n"); + ret = data->parent_irq; + goto out_unmap; + } + + data->domain = irq_domain_add_linear(np, 32, + &irq_generic_chip_ops, NULL); + if (!data->domain) { + ret = -ENOMEM; + goto out_unmap; + } + + /* Allocate a single Generic IRQ chip for this node */ + ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, + np->full_name, handle_edge_irq, clr, 0, 0); + if (ret) { + pr_err("failed to allocate generic irq chip\n"); + goto out_free_domain; + } + + /* Set the IRQ chaining logic */ + irq_set_handler_data(data->parent_irq, data); + irq_set_chained_handler(data->parent_irq, brcmstb_l2_intc_irq_handle); + + gc = irq_get_domain_generic_chip(data->domain, 0); + gc->reg_base = data->base; + gc->private = data; + ct = gc->chip_types; + + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->regs.ack = CPU_CLEAR; + + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->regs.disable = CPU_MASK_SET; + + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + ct->regs.enable = CPU_MASK_CLEAR; + + ct->chip.irq_suspend = brcmstb_l2_intc_suspend; + ct->chip.irq_resume = brcmstb_l2_intc_resume; + + if (of_property_read_bool(np, "brcm,irq-can-wake")) { + data->can_wake = true; + /* This IRQ chip can wake the system, set all child interrupts + * in wake_enabled mask + */ + gc->wake_enabled = 0xffffffff; + ct->chip.irq_set_wake = irq_gc_set_wake; + } + + pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n", + data->base, data->parent_irq); + + return 0; + +out_free_domain: + irq_domain_remove(data->domain); +out_unmap: + iounmap(data->base); +out_free: + kfree(data); + return ret; +} +IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_intc_of_init); diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c new file mode 100644 index 00000000000..33340dc97d1 --- /dev/null +++ b/drivers/irqchip/irq-clps711x.c @@ -0,0 +1,243 @@ +/* + * CLPS711X IRQ driver + * + * Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/slab.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#include "irqchip.h" + +#define CLPS711X_INTSR1 (0x0240) +#define CLPS711X_INTMR1 (0x0280) +#define CLPS711X_BLEOI (0x0600) +#define CLPS711X_MCEOI (0x0640) +#define CLPS711X_TEOI (0x0680) +#define CLPS711X_TC1EOI (0x06c0) +#define CLPS711X_TC2EOI (0x0700) +#define CLPS711X_RTCEOI (0x0740) +#define CLPS711X_UMSEOI (0x0780) +#define CLPS711X_COEOI (0x07c0) +#define CLPS711X_INTSR2 (0x1240) +#define CLPS711X_INTMR2 (0x1280) +#define CLPS711X_SRXEOF (0x1600) +#define CLPS711X_KBDEOI (0x1700) +#define CLPS711X_INTSR3 (0x2240) +#define CLPS711X_INTMR3 (0x2280) + +static const struct { +#define CLPS711X_FLAG_EN (1 << 0) +#define CLPS711X_FLAG_FIQ (1 << 1) + unsigned int flags; + phys_addr_t eoi; +} clps711x_irqs[] = { + [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, }, + [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, }, + [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, }, + [5] = { CLPS711X_FLAG_EN, }, + [6] = { CLPS711X_FLAG_EN, }, + [7] = { CLPS711X_FLAG_EN, }, + [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, }, + [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, }, + [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, }, + [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, }, + [12] = { CLPS711X_FLAG_EN, }, + [13] = { CLPS711X_FLAG_EN, }, + [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, }, + [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, }, + [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, }, + [17] = { CLPS711X_FLAG_EN, }, + [18] = { CLPS711X_FLAG_EN, }, + [28] = { CLPS711X_FLAG_EN, }, + [29] = { CLPS711X_FLAG_EN, }, + [32] = { CLPS711X_FLAG_FIQ, }, +}; + +static struct { + void __iomem *base; + void __iomem *intmr[3]; + void __iomem *intsr[3]; + struct irq_domain *domain; + struct irq_domain_ops ops; +} *clps711x_intc; + +static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs) +{ + u32 irqnr, irqstat; + + do { + irqstat = readw_relaxed(clps711x_intc->intmr[0]) & + readw_relaxed(clps711x_intc->intsr[0]); + if (irqstat) { + irqnr = irq_find_mapping(clps711x_intc->domain, + fls(irqstat) - 1); + handle_IRQ(irqnr, regs); + } + + irqstat = readw_relaxed(clps711x_intc->intmr[1]) & + readw_relaxed(clps711x_intc->intsr[1]); + if (irqstat) { + irqnr = irq_find_mapping(clps711x_intc->domain, + fls(irqstat) - 1 + 16); + handle_IRQ(irqnr, regs); + } + } while (irqstat); +} + +static void clps711x_intc_eoi(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi); +} + +static void clps711x_intc_mask(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; + u32 tmp; + + tmp = readl_relaxed(intmr); + tmp &= ~(1 << (hwirq % 16)); + writel_relaxed(tmp, intmr); +} + +static void clps711x_intc_unmask(struct irq_data *d) +{ + irq_hw_number_t hwirq = irqd_to_hwirq(d); + void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; + u32 tmp; + + tmp = readl_relaxed(intmr); + tmp |= 1 << (hwirq % 16); + writel_relaxed(tmp, intmr); +} + +static struct irq_chip clps711x_intc_chip = { + .name = "clps711x-intc", + .irq_eoi = clps711x_intc_eoi, + .irq_mask = clps711x_intc_mask, + .irq_unmask = clps711x_intc_unmask, +}; + +static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_flow_handler_t handler = handle_level_irq; + unsigned int flags = IRQF_VALID | IRQF_PROBE; + + if (!clps711x_irqs[hw].flags) + return 0; + + if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) { + handler = handle_bad_irq; + flags |= IRQF_NOAUTOEN; + } else if (clps711x_irqs[hw].eoi) { + handler = handle_fasteoi_irq; + } + + /* Clear down pending interrupt */ + if (clps711x_irqs[hw].eoi) + writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi); + + irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler); + set_irq_flags(virq, flags); + + return 0; +} + +static int __init _clps711x_intc_init(struct device_node *np, + phys_addr_t base, resource_size_t size) +{ + int err; + + clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL); + if (!clps711x_intc) + return -ENOMEM; + + clps711x_intc->base = ioremap(base, size); + if (!clps711x_intc->base) { + err = -ENOMEM; + goto out_kfree; + } + + clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1; + clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1; + clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2; + clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2; + clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3; + clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3; + + /* Mask all interrupts */ + writel_relaxed(0, clps711x_intc->intmr[0]); + writel_relaxed(0, clps711x_intc->intmr[1]); + writel_relaxed(0, clps711x_intc->intmr[2]); + + err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id()); + if (IS_ERR_VALUE(err)) + goto out_iounmap; + + clps711x_intc->ops.map = clps711x_intc_irq_map; + clps711x_intc->ops.xlate = irq_domain_xlate_onecell; + clps711x_intc->domain = + irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs), + 0, 0, &clps711x_intc->ops, NULL); + if (!clps711x_intc->domain) { + err = -ENOMEM; + goto out_irqfree; + } + + irq_set_default_host(clps711x_intc->domain); + set_handle_irq(clps711x_irqh); + +#ifdef CONFIG_FIQ + init_FIQ(0); +#endif + + return 0; + +out_irqfree: + irq_free_descs(0, ARRAY_SIZE(clps711x_irqs)); + +out_iounmap: + iounmap(clps711x_intc->base); + +out_kfree: + kfree(clps711x_intc); + + return err; +} + +void __init clps711x_intc_init(phys_addr_t base, resource_size_t size) +{ + BUG_ON(_clps711x_intc_init(NULL, base, size)); +} + +#ifdef CONFIG_IRQCHIP +static int __init clps711x_intc_init_dt(struct device_node *np, + struct device_node *parent) +{ + struct resource res; + int err; + + err = of_address_to_resource(np, 0, &res); + if (err) + return err; + + return _clps711x_intc_init(np, res.start, resource_size(&res)); +} +IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt); +#endif diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c new file mode 100644 index 00000000000..3d15d16a708 --- /dev/null +++ b/drivers/irqchip/irq-crossbar.c @@ -0,0 +1,208 @@ +/* + * drivers/irqchip/irq-crossbar.c + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * Author: Sricharan R <r.sricharan@ti.com> + * + * 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. + * + */ +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/slab.h> +#include <linux/irqchip/arm-gic.h> + +#define IRQ_FREE -1 +#define GIC_IRQ_START 32 + +/* + * @int_max: maximum number of supported interrupts + * @irq_map: array of interrupts to crossbar number mapping + * @crossbar_base: crossbar base address + * @register_offsets: offsets for each irq number + */ +struct crossbar_device { + uint int_max; + uint *irq_map; + void __iomem *crossbar_base; + int *register_offsets; + void (*write) (int, int); +}; + +static struct crossbar_device *cb; + +static inline void crossbar_writel(int irq_no, int cb_no) +{ + writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline void crossbar_writew(int irq_no, int cb_no) +{ + writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline void crossbar_writeb(int irq_no, int cb_no) +{ + writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); +} + +static inline int allocate_free_irq(int cb_no) +{ + int i; + + for (i = 0; i < cb->int_max; i++) { + if (cb->irq_map[i] == IRQ_FREE) { + cb->irq_map[i] = cb_no; + return i; + } + } + + return -ENODEV; +} + +static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + return 0; +} + +static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; + + if (hw > GIC_IRQ_START) + cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; +} + +static int crossbar_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + unsigned long ret; + + ret = allocate_free_irq(intspec[1]); + + if (IS_ERR_VALUE(ret)) + return ret; + + *out_hwirq = ret + GIC_IRQ_START; + return 0; +} + +const struct irq_domain_ops routable_irq_domain_ops = { + .map = crossbar_domain_map, + .unmap = crossbar_domain_unmap, + .xlate = crossbar_domain_xlate +}; + +static int __init crossbar_of_init(struct device_node *node) +{ + int i, size, max, reserved = 0, entry; + const __be32 *irqsr; + + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + + if (!cb) + return -ENOMEM; + + cb->crossbar_base = of_iomap(node, 0); + if (!cb->crossbar_base) + goto err1; + + of_property_read_u32(node, "ti,max-irqs", &max); + cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); + if (!cb->irq_map) + goto err2; + + cb->int_max = max; + + for (i = 0; i < max; i++) + cb->irq_map[i] = IRQ_FREE; + + /* Get and mark reserved irqs */ + irqsr = of_get_property(node, "ti,irqs-reserved", &size); + if (irqsr) { + size /= sizeof(__be32); + + for (i = 0; i < size; i++) { + of_property_read_u32_index(node, + "ti,irqs-reserved", + i, &entry); + if (entry > max) { + pr_err("Invalid reserved entry\n"); + goto err3; + } + cb->irq_map[entry] = 0; + } + } + + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); + if (!cb->register_offsets) + goto err3; + + of_property_read_u32(node, "ti,reg-size", &size); + + switch (size) { + case 1: + cb->write = crossbar_writeb; + break; + case 2: + cb->write = crossbar_writew; + break; + case 4: + cb->write = crossbar_writel; + break; + default: + pr_err("Invalid reg-size property\n"); + goto err4; + break; + } + + /* + * Register offsets are not linear because of the + * reserved irqs. so find and store the offsets once. + */ + for (i = 0; i < max; i++) { + if (!cb->irq_map[i]) + continue; + + cb->register_offsets[i] = reserved; + reserved += size; + } + + register_routable_domain_ops(&routable_irq_domain_ops); + return 0; + +err4: + kfree(cb->register_offsets); +err3: + kfree(cb->irq_map); +err2: + iounmap(cb->crossbar_base); +err1: + kfree(cb); + return -ENOMEM; +} + +static const struct of_device_id crossbar_match[] __initconst = { + { .compatible = "ti,irq-crossbar" }, + {} +}; + +int __init irqcrossbar_init(void) +{ + struct device_node *np; + np = of_find_matching_node(NULL, crossbar_match); + if (!np) + return -ENODEV; + + crossbar_of_init(np); + return 0; +} diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 531769b2433..7c131cf7cc1 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -42,6 +42,7 @@ #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> +#include <asm/cputype.h> #include <asm/irq.h> #include <asm/exception.h> #include <asm/smp_plat.h> @@ -246,10 +247,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); - unsigned int shift = (gic_irq(d) % 4) * 8; - unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); + unsigned int cpu, shift = (gic_irq(d) % 4) * 8; u32 val, mask, bit; + if (!force) + cpu = cpumask_any_and(mask_val, cpu_online_mask); + else + cpu = cpumask_first(mask_val); + if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) return -EINVAL; @@ -287,7 +292,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); @@ -661,9 +666,9 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) /* * Ensure that stores to Normal memory are visible to the - * other CPUs before issuing the IPI. + * other CPUs before they observe us issuing the IPI. */ - dsb(); + dmb(ishst); /* this always happens on GIC0 */ writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); @@ -824,16 +829,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + + gic_routable_irq_domain_ops->map(d, irq, hw); } irq_set_chip_data(irq, d->host_data); return 0; } +static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + gic_routable_irq_domain_ops->unmap(d, irq); +} + static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *controller, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type) { + unsigned long ret = 0; + if (d->of_node != controller) return -EINVAL; if (intsize < 3) @@ -843,11 +857,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d, *out_hwirq = intspec[1] + 16; /* For SPIs, we need to add 16 more to get the GIC irq ID number */ - if (!intspec[0]) - *out_hwirq += 16; + if (!intspec[0]) { + ret = gic_routable_irq_domain_ops->xlate(d, controller, + intspec, + intsize, + out_hwirq, + out_type); + + if (IS_ERR_VALUE(ret)) + return ret; + } *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; - return 0; + + return ret; } #ifdef CONFIG_SMP @@ -871,9 +894,41 @@ static struct notifier_block gic_cpu_notifier = { static const struct irq_domain_ops gic_irq_domain_ops = { .map = gic_irq_domain_map, + .unmap = gic_irq_domain_unmap, .xlate = gic_irq_domain_xlate, }; +/* Default functions for routable irq domain */ +static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + return 0; +} + +static void gic_routable_irq_domain_unmap(struct irq_domain *d, + unsigned int irq) +{ +} + +static int gic_routable_irq_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + *out_hwirq += 16; + return 0; +} + +const struct irq_domain_ops gic_default_routable_irq_domain_ops = { + .map = gic_routable_irq_domain_map, + .unmap = gic_routable_irq_domain_unmap, + .xlate = gic_routable_irq_domain_xlate, +}; + +const struct irq_domain_ops *gic_routable_irq_domain_ops = + &gic_default_routable_irq_domain_ops; + void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node) @@ -881,6 +936,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; + int nr_routable_irqs; BUG_ON(gic_nr >= MAX_GIC_NR); @@ -899,7 +955,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, } for_each_possible_cpu(cpu) { - unsigned long offset = percpu_offset * cpu_logical_map(cpu); + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + unsigned long offset = percpu_offset * core_id; *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; } @@ -946,14 +1004,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic->gic_irqs = gic_irqs; gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ - irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); - if (IS_ERR_VALUE(irq_base)) { - WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", - irq_start); - irq_base = irq_start; + + if (of_property_read_u32(node, "arm,routable-irqs", + &nr_routable_irqs)) { + irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, + numa_node_id()); + if (IS_ERR_VALUE(irq_base)) { + WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", + irq_start); + irq_base = irq_start; + } + + gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, + hwirq_base, &gic_irq_domain_ops, gic); + } else { + gic->domain = irq_domain_add_linear(node, nr_routable_irqs, + &gic_irq_domain_ops, + gic); } - gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, - hwirq_base, &gic_irq_domain_ops, gic); + if (WARN_ON(!gic->domain)) return; @@ -1005,8 +1074,10 @@ gic_of_init(struct device_node *node, struct device_node *parent) gic_cnt++; return 0; } +IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init); IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); +IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init); IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 3c8827fe83f..1c3e2c9b46b 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -22,7 +22,7 @@ #include <linux/of_irq.h> #include <asm/exception.h> -#include <asm/mach/irq.h> +#include <asm/hardirq.h> #include "irqchip.h" diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index 63b3d4eb0ef..4044ff28766 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -96,7 +96,7 @@ static struct irq_domain_ops icoll_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; -static void __init icoll_of_init(struct device_node *np, +static int __init icoll_of_init(struct device_node *np, struct device_node *interrupt_parent) { icoll_base = of_iomap(np, 0); @@ -110,6 +110,6 @@ static void __init icoll_of_init(struct device_node *np, icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS, &icoll_irq_domain_ops, NULL); - WARN_ON(!icoll_domain); + return icoll_domain ? 0 : -ENODEV; } IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init); diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index e25f246cd2f..34d18b48bb7 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; + u32 hwirq = __fls(stat); u32 irq = irq_find_mapping(orion_irq_domain, gc->irq_base + hwirq); handle_IRQ(irq, regs); @@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; + u32 hwirq = __fls(stat); generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); stat &= ~(1 << hwirq); diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index bbcc944ed94..78a6accd205 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -1323,8 +1323,7 @@ static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { }; int __init s3c2410_init_intc_of(struct device_node *np, - struct device_node *interrupt_parent, - struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) + struct device_node *interrupt_parent) { return s3c_init_intc_of(np, interrupt_parent, s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); @@ -1346,8 +1345,7 @@ static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { }; int __init s3c2416_init_intc_of(struct device_node *np, - struct device_node *interrupt_parent, - struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) + struct device_node *interrupt_parent) { return s3c_init_intc_of(np, interrupt_parent, s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 581eefe331a..5e54f6d71e7 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -58,7 +58,8 @@ static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) handle_IRQ(irqnr, regs); } -static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *parent) +static int __init sirfsoc_irq_init(struct device_node *np, + struct device_node *parent) { void __iomem *base = of_iomap(np, 0); if (!base) diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 473f09a74d4..7d35287f9e9 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -24,6 +24,7 @@ #include <linux/list.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> @@ -57,6 +58,7 @@ /** * struct vic_device - VIC PM device + * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0. * @irq: The IRQ number for the base of the VIC. * @base: The register base for the VIC. * @valid_sources: A bitmask of valid interrupts @@ -224,6 +226,22 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) return handled; } +static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc) +{ + u32 stat, hwirq; + struct irq_chip *host_chip = irq_desc_get_chip(desc); + struct vic_device *vic = irq_desc_get_handler_data(desc); + + chained_irq_enter(host_chip, desc); + + while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { + hwirq = ffs(stat) - 1; + generic_handle_irq(irq_find_mapping(vic->domain, hwirq)); + } + + chained_irq_exit(host_chip, desc); +} + /* * Keep iterating over all registered VIC's until there are no pending * interrupts. @@ -246,6 +264,7 @@ static struct irq_domain_ops vic_irqdomain_ops = { /** * vic_register() - Register a VIC. * @base: The base address of the VIC. + * @parent_irq: The parent IRQ if cascaded, else 0. * @irq: The base IRQ for the VIC. * @valid_sources: bitmask of valid interrupts * @resume_sources: bitmask of interrupts allowed for resume sources. @@ -257,7 +276,8 @@ static struct irq_domain_ops vic_irqdomain_ops = { * * This also configures the IRQ domain for the VIC. */ -static void __init vic_register(void __iomem *base, unsigned int irq, +static void __init vic_register(void __iomem *base, unsigned int parent_irq, + unsigned int irq, u32 valid_sources, u32 resume_sources, struct device_node *node) { @@ -273,15 +293,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq, v->base = base; v->valid_sources = valid_sources; v->resume_sources = resume_sources; - v->irq = irq; set_handle_irq(vic_handle_irq); vic_id++; + + if (parent_irq) { + irq_set_handler_data(parent_irq, v); + irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded); + } + v->domain = irq_domain_add_simple(node, fls(valid_sources), irq, &vic_irqdomain_ops, v); /* create an IRQ mapping for each valid IRQ */ for (i = 0; i < fls(valid_sources); i++) if (valid_sources & (1 << i)) irq_create_mapping(v->domain, i); + /* If no base IRQ was passed, figure out our allocated base */ + if (irq) + v->irq = irq; + else + v->irq = irq_find_mapping(v->domain, 0); } static void vic_ack_irq(struct irq_data *d) @@ -409,10 +439,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, writel(32, base + VIC_PL190_DEF_VECT_ADDR); } - vic_register(base, irq_start, vic_sources, 0, node); + vic_register(base, 0, irq_start, vic_sources, 0, node); } -void __init __vic_init(void __iomem *base, int irq_start, +void __init __vic_init(void __iomem *base, int parent_irq, int irq_start, u32 vic_sources, u32 resume_sources, struct device_node *node) { @@ -449,7 +479,7 @@ void __init __vic_init(void __iomem *base, int irq_start, vic_init2(base); - vic_register(base, irq_start, vic_sources, resume_sources, node); + vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node); } /** @@ -462,8 +492,30 @@ void __init __vic_init(void __iomem *base, int irq_start, void __init vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources, u32 resume_sources) { - __vic_init(base, irq_start, vic_sources, resume_sources, NULL); + __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL); +} + +/** + * vic_init_cascaded() - initialise a cascaded vectored interrupt controller + * @base: iomem base address + * @parent_irq: the parent IRQ we're cascaded off + * @irq_start: starting interrupt number, must be muliple of 32 + * @vic_sources: bitmask of interrupt sources to allow + * @resume_sources: bitmask of interrupt sources to allow for resume + * + * This returns the base for the new interrupts or negative on error. + */ +int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq, + u32 vic_sources, u32 resume_sources) +{ + struct vic_device *v; + + v = &vic_devices[vic_id]; + __vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL); + /* Return out acquired base */ + return v->irq; } +EXPORT_SYMBOL_GPL(vic_init_cascaded); #ifdef CONFIG_OF int __init vic_of_init(struct device_node *node, struct device_node *parent) @@ -485,7 +537,7 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent) /* * Passing 0 as first IRQ makes the simple domain allocate descriptors */ - __vic_init(regs, 0, interrupt_mask, wakeup_mask, node); + __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node); return 0; } diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index cad3e249555..0fe2f718d81 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -19,11 +19,11 @@ * special section. */ static const struct of_device_id -irqchip_of_match_end __used __section(__irqchip_of_end); +irqchip_of_match_end __used __section(__irqchip_of_table_end); -extern struct of_device_id __irqchip_begin[]; +extern struct of_device_id __irqchip_of_table[]; void __init irqchip_init(void) { - of_irq_init(__irqchip_begin); + of_irq_init(__irqchip_of_table); } diff --git a/drivers/irqchip/irqchip.h b/drivers/irqchip/irqchip.h index e445ba2d6ad..0f6486d4f1b 100644 --- a/drivers/irqchip/irqchip.h +++ b/drivers/irqchip/irqchip.h @@ -11,6 +11,8 @@ #ifndef _IRQCHIP_H #define _IRQCHIP_H +#include <linux/of.h> + /* * This macro must be used by the different irqchip drivers to declare * the association between their DT compatible string and their @@ -21,9 +23,6 @@ * @compstr: compatible string of the irqchip driver * @fn: initialization function */ -#define IRQCHIP_DECLARE(name,compstr,fn) \ - static const struct of_device_id irqchip_of_match_##name \ - __used __section(__irqchip_of_table) \ - = { .compatible = compstr, .data = fn } +#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn) #endif diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 8527743b5ce..6ce6bd3441b 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -5,7 +5,7 @@ * Viresh Kumar <viresh.linux@gmail.com> * * Copyright (C) 2012 ST Microelectronics - * Shiraz Hashim <shiraz.hashim@st.com> + * Shiraz Hashim <shiraz.linux.kernel@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -125,7 +125,7 @@ static struct spear_shirq spear320_shirq_ras2 = { }; static struct spear_shirq spear320_shirq_ras3 = { - .irq_nr = 3, + .irq_nr = 7, .irq_bit_off = 0, .invalid_irq = 1, .regs = { |
