aboutsummaryrefslogtreecommitdiff
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig45
-rw-r--r--drivers/irqchip/Makefile9
-rw-r--r--drivers/irqchip/exynos-combiner.c3
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c95
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c254
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.h39
-rw-r--r--drivers/irqchip/irq-atmel-aic.c262
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c353
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c202
-rw-r--r--drivers/irqchip/irq-clps711x.c243
-rw-r--r--drivers/irqchip/irq-crossbar.c326
-rw-r--r--drivers/irqchip/irq-gic-common.c115
-rw-r--r--drivers/irqchip/irq-gic-common.h29
-rw-r--r--drivers/irqchip/irq-gic-v3.c692
-rw-r--r--drivers/irqchip/irq-gic.c162
-rw-r--r--drivers/irqchip/irq-mmp.c2
-rw-r--r--drivers/irqchip/irq-mxs.c4
-rw-r--r--drivers/irqchip/irq-nvic.c13
-rw-r--r--drivers/irqchip/irq-or1k-pic.c182
-rw-r--r--drivers/irqchip/irq-orion.c4
-rw-r--r--drivers/irqchip/irq-s3c24xx.c6
-rw-r--r--drivers/irqchip/irq-sirfsoc.c3
-rw-r--r--drivers/irqchip/irq-vic.c66
-rw-r--r--drivers/irqchip/irqchip.c6
-rw-r--r--drivers/irqchip/irqchip.h7
-rw-r--r--drivers/irqchip/spear-shirq.c306
26 files changed, 3115 insertions, 313 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 61ffdca96e25..4e230e7c76ee 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -10,6 +10,11 @@ config ARM_GIC
config GIC_NON_BANKED
bool
+config ARM_GIC_V3
+ bool
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+
config ARM_NVIC
bool
select IRQ_DOMAIN
@@ -30,6 +35,26 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for
power management.
+config ATMEL_AIC_IRQ
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+
+config ATMEL_AIC5_IRQ
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+
+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 +64,18 @@ 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 OR1K_PIC
+ bool
+ select IRQ_DOMAIN
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
@@ -69,3 +106,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 1c0c151d108c..73052ba9ca62 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -10,13 +10,18 @@ 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_OR1K_PIC) += irq-or1k-pic.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
-obj-$(CONFIG_ARM_GIC) += irq-gic.o
+obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
+obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
obj-$(CONFIG_ARM_VIC) += irq-vic.o
+obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
+obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
@@ -27,3 +32,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 40e6440348ff..f8636a650cf6 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 41be897df8d5..574aba0eba4e 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-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
new file mode 100644
index 000000000000..6ae3cdee0681
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -0,0 +1,254 @@
+/*
+ * Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by
+ * irq-atmel-aic and irq-atmel-aic5 drivers
+ *
+ * Copyright (C) 2004 SAN People
+ * Copyright (C) 2004 ATMEL
+ * Copyright (C) Rick Bronson
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include "irq-atmel-aic-common.h"
+
+#define AT91_AIC_PRIOR GENMASK(2, 0)
+#define AT91_AIC_IRQ_MIN_PRIORITY 0
+#define AT91_AIC_IRQ_MAX_PRIORITY 7
+
+#define AT91_AIC_SRCTYPE GENMASK(7, 6)
+#define AT91_AIC_SRCTYPE_LOW (0 << 5)
+#define AT91_AIC_SRCTYPE_FALLING (1 << 5)
+#define AT91_AIC_SRCTYPE_HIGH (2 << 5)
+#define AT91_AIC_SRCTYPE_RISING (3 << 5)
+
+struct aic_chip_data {
+ u32 ext_irqs;
+};
+
+static void aic_common_shutdown(struct irq_data *d)
+{
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
+ ct->chip.irq_mask(d);
+}
+
+int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct aic_chip_data *aic = gc->private;
+ unsigned aic_type;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ aic_type = AT91_AIC_SRCTYPE_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ aic_type = AT91_AIC_SRCTYPE_RISING;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ if (!(d->mask & aic->ext_irqs))
+ return -EINVAL;
+
+ aic_type = AT91_AIC_SRCTYPE_LOW;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ if (!(d->mask & aic->ext_irqs))
+ return -EINVAL;
+
+ aic_type = AT91_AIC_SRCTYPE_FALLING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *val &= AT91_AIC_SRCTYPE;
+ *val |= aic_type;
+
+ return 0;
+}
+
+int aic_common_set_priority(int priority, unsigned *val)
+{
+ if (priority < AT91_AIC_IRQ_MIN_PRIORITY ||
+ priority > AT91_AIC_IRQ_MAX_PRIORITY)
+ return -EINVAL;
+
+ *val &= AT91_AIC_PRIOR;
+ *val |= priority;
+
+ return 0;
+}
+
+int aic_common_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec,
+ unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 3))
+ return -EINVAL;
+
+ if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) ||
+ (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY)))
+ return -EINVAL;
+
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+ return 0;
+}
+
+static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
+{
+ struct device_node *node = domain->of_node;
+ struct irq_chip_generic *gc;
+ struct aic_chip_data *aic;
+ struct property *prop;
+ const __be32 *p;
+ u32 hwirq;
+
+ gc = irq_get_domain_generic_chip(domain, 0);
+
+ aic = gc->private;
+ aic->ext_irqs |= 1;
+
+ of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) {
+ gc = irq_get_domain_generic_chip(domain, hwirq);
+ if (!gc) {
+ pr_warn("AIC: external irq %d >= %d skip it\n",
+ hwirq, domain->revmap_size);
+ continue;
+ }
+
+ aic = gc->private;
+ aic->ext_irqs |= (1 << (hwirq % 32));
+ }
+}
+
+#define AT91_RTC_IDR 0x24
+#define AT91_RTC_IMR 0x28
+#define AT91_RTC_IRQ_MASK 0x1f
+
+void __init aic_common_rtc_irq_fixup(struct device_node *root)
+{
+ struct device_node *np;
+ void __iomem *regs;
+
+ np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc");
+ if (!np)
+ np = of_find_compatible_node(root, NULL,
+ "atmel,at91sam9x5-rtc");
+
+ if (!np)
+ return;
+
+ regs = of_iomap(np, 0);
+ of_node_put(np);
+
+ if (!regs)
+ return;
+
+ writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR);
+
+ iounmap(regs);
+}
+
+void __init aic_common_irq_fixup(const struct of_device_id *matches)
+{
+ struct device_node *root = of_find_node_by_path("/");
+ const struct of_device_id *match;
+
+ if (!root)
+ return;
+
+ match = of_match_node(matches, root);
+ of_node_put(root);
+
+ if (match) {
+ void (*fixup)(struct device_node *) = match->data;
+ fixup(root);
+ }
+
+ of_node_put(root);
+}
+
+struct irq_domain *__init aic_common_of_init(struct device_node *node,
+ const struct irq_domain_ops *ops,
+ const char *name, int nirqs)
+{
+ struct irq_chip_generic *gc;
+ struct irq_domain *domain;
+ struct aic_chip_data *aic;
+ void __iomem *reg_base;
+ int nchips;
+ int ret;
+ int i;
+
+ nchips = DIV_ROUND_UP(nirqs, 32);
+
+ reg_base = of_iomap(node, 0);
+ if (!reg_base)
+ return ERR_PTR(-ENOMEM);
+
+ aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL);
+ if (!aic) {
+ ret = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ domain = irq_domain_add_linear(node, nchips * 32, ops, aic);
+ if (!domain) {
+ ret = -ENOMEM;
+ goto err_free_aic;
+ }
+
+ ret = irq_alloc_domain_generic_chips(domain, 32, 1, name,
+ handle_level_irq, 0, 0,
+ IRQCHIP_SKIP_SET_WAKE);
+ if (ret)
+ goto err_domain_remove;
+
+ for (i = 0; i < nchips; i++) {
+ gc = irq_get_domain_generic_chip(domain, i * 32);
+
+ gc->reg_base = reg_base;
+
+ gc->unused = 0;
+ gc->wake_enabled = ~0;
+ gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK;
+ gc->chip_types[0].handler = handle_fasteoi_irq;
+ gc->chip_types[0].chip.irq_eoi = irq_gc_eoi;
+ gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
+ gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown;
+ gc->private = &aic[i];
+ }
+
+ aic_common_ext_irq_of_init(domain);
+
+ return domain;
+
+err_domain_remove:
+ irq_domain_remove(domain);
+
+err_free_aic:
+ kfree(aic);
+
+err_iounmap:
+ iounmap(reg_base);
+
+ return ERR_PTR(ret);
+}
diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h
new file mode 100644
index 000000000000..90aa00e918d6
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic-common.h
@@ -0,0 +1,39 @@
+/*
+ * Atmel AT91 common AIC (Advanced Interrupt Controller) header file
+ *
+ * Copyright (C) 2004 SAN People
+ * Copyright (C) 2004 ATMEL
+ * Copyright (C) Rick Bronson
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __IRQ_ATMEL_AIC_COMMON_H
+#define __IRQ_ATMEL_AIC_COMMON_H
+
+
+int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val);
+
+int aic_common_set_priority(int priority, unsigned *val);
+
+int aic_common_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec,
+ unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type);
+
+struct irq_domain *__init aic_common_of_init(struct device_node *node,
+ const struct irq_domain_ops *ops,
+ const char *name, int nirqs);
+
+void __init aic_common_rtc_irq_fixup(struct device_node *root);
+
+void __init aic_common_irq_fixup(const struct of_device_id *matches);
+
+#endif /* __IRQ_ATMEL_AIC_COMMON_H */
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
new file mode 100644
index 000000000000..a82869e9fb26
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -0,0 +1,262 @@
+/*
+ * Atmel AT91 AIC (Advanced Interrupt Controller) driver
+ *
+ * Copyright (C) 2004 SAN People
+ * Copyright (C) 2004 ATMEL
+ * Copyright (C) Rick Bronson
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/bitmap.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irq-atmel-aic-common.h"
+#include "irqchip.h"
+
+/* Number of irq lines managed by AIC */
+#define NR_AIC_IRQS 32
+
+#define AT91_AIC_SMR(n) ((n) * 4)
+
+#define AT91_AIC_SVR(n) (0x80 + ((n) * 4))
+#define AT91_AIC_IVR 0x100
+#define AT91_AIC_FVR 0x104
+#define AT91_AIC_ISR 0x108
+
+#define AT91_AIC_IPR 0x10c
+#define AT91_AIC_IMR 0x110
+#define AT91_AIC_CISR 0x114
+
+#define AT91_AIC_IECR 0x120
+#define AT91_AIC_IDCR 0x124
+#define AT91_AIC_ICCR 0x128
+#define AT91_AIC_ISCR 0x12c
+#define AT91_AIC_EOICR 0x130
+#define AT91_AIC_SPU 0x134
+#define AT91_AIC_DCR 0x138
+
+static struct irq_domain *aic_domain;
+
+static asmlinkage void __exception_irq_entry
+aic_handle(struct pt_regs *regs)
+{
+ struct irq_domain_chip_generic *dgc = aic_domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+ u32 irqnr;
+ u32 irqstat;
+
+ irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
+ irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
+
+ irqnr = irq_find_mapping(aic_domain, irqnr);
+
+ if (!irqstat)
+ irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
+ else
+ handle_IRQ(irqnr, regs);
+}
+
+static int aic_retrigger(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+
+ /* Enable interrupt on AIC5 */
+ irq_gc_lock(gc);
+ irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR);
+ irq_gc_unlock(gc);
+
+ return 0;
+}
+
+static int aic_set_type(struct irq_data *d, unsigned type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ unsigned int smr;
+ int ret;
+
+ smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq));
+ ret = aic_common_set_type(d, type, &smr);
+ if (ret)
+ return ret;
+
+ irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void aic_suspend(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+
+ irq_gc_lock(gc);
+ irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR);
+ irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR);
+ irq_gc_unlock(gc);
+}
+
+static void aic_resume(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+
+ irq_gc_lock(gc);
+ irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR);
+ irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR);
+ irq_gc_unlock(gc);
+}
+
+static void aic_pm_shutdown(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+
+ irq_gc_lock(gc);
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
+ irq_gc_unlock(gc);
+}
+#else
+#define aic_suspend NULL
+#define aic_resume NULL
+#define aic_pm_shutdown NULL
+#endif /* CONFIG_PM */
+
+static void __init aic_hw_init(struct irq_domain *domain)
+{
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
+ int i;
+
+ /*
+ * Perform 8 End Of Interrupt Command to make sure AIC
+ * will not Lock out nIRQ
+ */
+ for (i = 0; i < 8; i++)
+ irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
+
+ /*
+ * Spurious Interrupt ID in Spurious Vector Register.
+ * When there is no current interrupt, the IRQ Vector Register
+ * reads the value stored in AIC_SPU
+ */
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU);
+
+ /* No debugging in AIC: Debug (Protect) Control Register */
+ irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR);
+
+ /* Disable and clear all interrupts initially */
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
+
+ for (i = 0; i < 32; i++)
+ irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i));
+}
+
+static int aic_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ struct irq_domain_chip_generic *dgc = d->gc;
+ struct irq_chip_generic *gc;
+ unsigned smr;
+ int idx;
+ int ret;
+
+ if (!dgc)
+ return -EINVAL;
+
+ ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
+ out_hwirq, out_type);
+ if (ret)
+ return ret;
+
+ idx = intspec[0] / dgc->irqs_per_chip;
+ if (idx >= dgc->num_chips)
+ return -EINVAL;
+
+ gc = dgc->gc[idx];
+
+ irq_gc_lock(gc);
+ smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq));
+ ret = aic_common_set_priority(intspec[2], &smr);
+ if (!ret)
+ irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq));
+ irq_gc_unlock(gc);
+
+ return ret;
+}
+
+static const struct irq_domain_ops aic_irq_ops = {
+ .map = irq_map_generic_chip,
+ .xlate = aic_irq_domain_xlate,
+};
+
+static void __init at91sam9_aic_irq_fixup(struct device_node *root)
+{
+ aic_common_rtc_irq_fixup(root);
+}
+
+static const struct of_device_id __initdata aic_irq_fixups[] = {
+ { .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup },
+ { .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup },
+ { .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup },
+ { .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup },
+ { /* sentinel */ },
+};
+
+static int __init aic_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_chip_generic *gc;
+ struct irq_domain *domain;
+
+ if (aic_domain)
+ return -EEXIST;
+
+ domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic",
+ NR_AIC_IRQS);
+ if (IS_ERR(domain))
+ return PTR_ERR(domain);
+
+ aic_common_irq_fixup(aic_irq_fixups);
+
+ aic_domain = domain;
+ gc = irq_get_domain_generic_chip(domain, 0);
+
+ gc->chip_types[0].regs.eoi = AT91_AIC_EOICR;
+ gc->chip_types[0].regs.enable = AT91_AIC_IECR;
+ gc->chip_types[0].regs.disable = AT91_AIC_IDCR;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
+ gc->chip_types[0].chip.irq_retrigger = aic_retrigger;
+ gc->chip_types[0].chip.irq_set_type = aic_set_type;
+ gc->chip_types[0].chip.irq_suspend = aic_suspend;
+ gc->chip_types[0].chip.irq_resume = aic_resume;
+ gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown;
+
+ aic_hw_init(domain);
+ set_handle_irq(aic_handle);
+
+ return 0;
+}
+IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init);
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
new file mode 100644
index 000000000000..edb227081524
--- /dev/null
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -0,0 +1,353 @@
+/*
+ * Atmel AT91 AIC5 (Advanced Interrupt Controller) driver
+ *
+ * Copyright (C) 2004 SAN People
+ * Copyright (C) 2004 ATMEL
+ * Copyright (C) Rick Bronson
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/bitmap.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irq-atmel-aic-common.h"
+#include "irqchip.h"
+
+/* Number of irq lines managed by AIC */
+#define NR_AIC5_IRQS 128
+
+#define AT91_AIC5_SSR 0x0
+#define AT91_AIC5_INTSEL_MSK (0x7f << 0)
+
+#define AT91_AIC5_SMR 0x4
+
+#define AT91_AIC5_SVR 0x8
+#define AT91_AIC5_IVR 0x10
+#define AT91_AIC5_FVR 0x14
+#define AT91_AIC5_ISR 0x18
+
+#define AT91_AIC5_IPR0 0x20
+#define AT91_AIC5_IPR1 0x24
+#define AT91_AIC5_IPR2 0x28
+#define AT91_AIC5_IPR3 0x2c
+#define AT91_AIC5_IMR 0x30
+#define AT91_AIC5_CISR 0x34
+
+#define AT91_AIC5_IECR 0x40
+#define AT91_AIC5_IDCR 0x44
+#define AT91_AIC5_ICCR 0x48
+#define AT91_AIC5_ISCR 0x4c
+#define AT91_AIC5_EOICR 0x38
+#define AT91_AIC5_SPU 0x3c
+#define AT91_AIC5_DCR 0x6c
+
+#define AT91_AIC5_FFER 0x50
+#define AT91_AIC5_FFDR 0x54
+#define AT91_AIC5_FFSR 0x58
+
+static struct irq_domain *aic5_domain;
+
+static asmlinkage void __exception_irq_entry
+aic5_handle(struct pt_regs *regs)
+{
+ struct irq_domain_chip_generic *dgc = aic5_domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+ u32 irqnr;
+ u32 irqstat;
+
+ irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
+ irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
+
+ irqnr = irq_find_mapping(aic5_domain, irqnr);
+
+ if (!irqstat)
+ irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
+ else
+ handle_IRQ(irqnr, regs);
+}
+
+static void aic5_mask(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+
+ /* Disable interrupt on AIC5 */
+ irq_gc_lock(gc);
+ irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
+ irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
+ gc->mask_cache &= ~d->mask;
+ irq_gc_unlock(gc);
+}
+
+static void aic5_unmask(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+
+ /* Enable interrupt on AIC5 */
+ irq_gc_lock(gc);
+ irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
+ irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR);
+ gc->mask_cache |= d->mask;
+ irq_gc_unlock(gc);
+}
+
+static int aic5_retrigger(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+
+ /* Enable interrupt on AIC5 */
+ irq_gc_lock(gc);
+ irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
+ irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR);
+ irq_gc_unlock(gc);
+
+ return 0;
+}
+
+static int aic5_set_type(struct irq_data *d, unsigned type)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *gc = dgc->gc[0];
+ unsigned int smr;
+ int ret;
+
+ irq_gc_lock(gc);
+ irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
+ smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
+ ret = aic_common_set_type(d, type, &smr);
+ if (!ret)
+ irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR);
+ irq_gc_unlock(gc);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static void aic5_suspend(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *bgc = dgc->gc[0];
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ int i;
+ u32 mask;
+
+ irq_gc_lock(bgc);
+ for (i = 0; i < dgc->irqs_per_chip; i++) {
+ mask = 1 << i;
+ if ((mask & gc->mask_cache) == (mask & gc->wake_active))
+ continue;
+
+ irq_reg_writel(i + gc->irq_base,
+ bgc->reg_base + AT91_AIC5_SSR);
+ if (mask & gc->wake_active)
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
+ else
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
+ }
+ irq_gc_unlock(bgc);
+}
+
+static void aic5_resume(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *bgc = dgc->gc[0];
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ int i;
+ u32 mask;
+
+ irq_gc_lock(bgc);
+ for (i = 0; i < dgc->irqs_per_chip; i++) {
+ mask = 1 << i;
+ if ((mask & gc->mask_cache) == (mask & gc->wake_active))
+ continue;
+
+ irq_reg_writel(i + gc->irq_base,
+ bgc->reg_base + AT91_AIC5_SSR);
+ if (mask & gc->mask_cache)
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
+ else
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
+ }
+ irq_gc_unlock(bgc);
+}
+
+static void aic5_pm_shutdown(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct irq_domain_chip_generic *dgc = domain->gc;
+ struct irq_chip_generic *bgc = dgc->gc[0];
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ int i;
+
+ irq_gc_lock(bgc);
+ for (i = 0; i < dgc->irqs_per_chip; i++) {
+ irq_reg_writel(i + gc->irq_base,
+ bgc->reg_base + AT91_AIC5_SSR);
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
+ irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR);
+ }
+ irq_gc_unlock(bgc);
+}
+#else
+#define aic5_suspend NULL
+#define aic5_resume NULL
+#define aic5_pm_shutdown NULL
+#endif /* CONFIG_PM */
+
+static void __init aic5_hw_init(struct irq_domain *domain)
+{
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
+ int i;
+
+ /*
+ * Perform 8 End Of Interrupt Command to make sure AIC
+ * will not Lock out nIRQ
+ */
+ for (i = 0; i < 8; i++)
+ irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
+
+ /*
+ * Spurious Interrupt ID in Spurious Vector Register.
+ * When there is no current interrupt, the IRQ Vector Register
+ * reads the value stored in AIC_SPU
+ */
+ irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU);
+
+ /* No debugging in AIC: Debug (Protect) Control Register */
+ irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR);
+
+ /* Disable and clear all interrupts initially */
+ for (i = 0; i < domain->revmap_size; i++) {
+ irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR);
+ irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR);
+ irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
+ irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR);
+ }
+}
+
+static int aic5_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ struct irq_domain_chip_generic *dgc = d->gc;
+ struct irq_chip_generic *gc;
+ unsigned smr;
+ int ret;
+
+ if (!dgc)
+ return -EINVAL;
+
+ ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
+ out_hwirq, out_type);
+ if (ret)
+ return ret;
+
+ gc = dgc->gc[0];
+
+ irq_gc_lock(gc);
+ irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR);
+ smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
+ ret = aic_common_set_priority(intspec[2], &smr);
+ if (!ret)
+ irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR);
+ irq_gc_unlock(gc);
+
+ return ret;
+}
+
+static const struct irq_domain_ops aic5_irq_ops = {
+ .map = irq_map_generic_chip,
+ .xlate = aic5_irq_domain_xlate,
+};
+
+static void __init sama5d3_aic_irq_fixup(struct device_node *root)
+{
+ aic_common_rtc_irq_fixup(root);
+}
+
+static const struct of_device_id __initdata aic5_irq_fixups[] = {
+ { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
+ { /* sentinel */ },
+};
+
+static int __init aic5_of_init(struct device_node *node,
+ struct device_node *parent,
+ int nirqs)
+{
+ struct irq_chip_generic *gc;
+ struct irq_domain *domain;
+ int nchips;
+ int i;
+
+ if (nirqs > NR_AIC5_IRQS)
+ return -EINVAL;
+
+ if (aic5_domain)
+ return -EEXIST;
+
+ domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
+ nirqs);
+ if (IS_ERR(domain))
+ return PTR_ERR(domain);
+
+ aic_common_irq_fixup(aic5_irq_fixups);
+
+ aic5_domain = domain;
+ nchips = aic5_domain->revmap_size / 32;
+ for (i = 0; i < nchips; i++) {
+ gc = irq_get_domain_generic_chip(domain, i * 32);
+
+ gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
+ gc->chip_types[0].chip.irq_mask = aic5_mask;
+ gc->chip_types[0].chip.irq_unmask = aic5_unmask;
+ gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
+ gc->chip_types[0].chip.irq_set_type = aic5_set_type;
+ gc->chip_types[0].chip.irq_suspend = aic5_suspend;
+ gc->chip_types[0].chip.irq_resume = aic5_resume;
+ gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
+ }
+
+ aic5_hw_init(domain);
+ set_handle_irq(aic5_handle);
+
+ return 0;
+}
+
+#define NR_SAMA5D3_IRQS 50
+
+static int __init sama5d3_aic5_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
+}
+IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
new file mode 100644
index 000000000000..c15c840987d2
--- /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 000000000000..33340dc97d1d
--- /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 000000000000..85c2985d8bcb
--- /dev/null
+++ b/drivers/irqchip/irq-crossbar.c
@@ -0,0 +1,326 @@
+/*
+ * 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>
+#include <linux/irqchip/irq-crossbar.h>
+
+#define IRQ_FREE -1
+#define IRQ_RESERVED -2
+#define IRQ_SKIP -3
+#define GIC_IRQ_START 32
+
+/**
+ * struct crossbar_device - crossbar device description
+ * @int_max: maximum number of supported interrupts
+ * @safe_map: safe default value to initialize the crossbar
+ * @max_crossbar_sources: Maximum number of crossbar sources
+ * @irq_map: array of interrupts to crossbar number mapping
+ * @crossbar_base: crossbar base address
+ * @register_offsets: offsets for each irq number
+ * @write: register write function pointer
+ */
+struct crossbar_device {
+ uint int_max;
+ uint safe_map;
+ uint max_crossbar_sources;
+ 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 get_prev_map_irq(int cb_no)
+{
+ int i;
+
+ for (i = cb->int_max - 1; i >= 0; i--)
+ if (cb->irq_map[i] == cb_no)
+ return i;
+
+ return -ENODEV;
+}
+
+static inline int allocate_free_irq(int cb_no)
+{
+ int i;
+
+ for (i = cb->int_max - 1; i >= 0; i--) {
+ if (cb->irq_map[i] == IRQ_FREE) {
+ cb->irq_map[i] = cb_no;
+ return i;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static inline bool needs_crossbar_write(irq_hw_number_t hw)
+{
+ int cb_no;
+
+ if (hw > GIC_IRQ_START) {
+ cb_no = cb->irq_map[hw - GIC_IRQ_START];
+ if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP)
+ return true;
+ }
+
+ return false;
+}
+
+static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ if (needs_crossbar_write(hw))
+ cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
+
+ return 0;
+}
+
+/**
+ * crossbar_domain_unmap - unmap a crossbar<->irq connection
+ * @d: domain of irq to unmap
+ * @irq: virq number
+ *
+ * We do not maintain a use count of total number of map/unmap
+ * calls for a particular irq to find out if a irq can be really
+ * unmapped. This is because unmap is called during irq_dispose_mapping(irq),
+ * after which irq is anyways unusable. So an explicit map has to be called
+ * after that.
+ */
+static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
+
+ if (needs_crossbar_write(hw)) {
+ cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
+ cb->write(hw - GIC_IRQ_START, cb->safe_map);
+ }
+}
+
+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)
+{
+ int ret;
+ int req_num = intspec[1];
+ int direct_map_num;
+
+ if (req_num >= cb->max_crossbar_sources) {
+ direct_map_num = req_num - cb->max_crossbar_sources;
+ if (direct_map_num < cb->int_max) {
+ ret = cb->irq_map[direct_map_num];
+ if (ret == IRQ_RESERVED || ret == IRQ_SKIP) {
+ /* We use the interrupt num as h/w irq num */
+ ret = direct_map_num;
+ goto found;
+ }
+ }
+
+ pr_err("%s: requested crossbar number %d > max %d\n",
+ __func__, req_num, cb->max_crossbar_sources);
+ return -EINVAL;
+ }
+
+ ret = get_prev_map_irq(req_num);
+ if (ret >= 0)
+ goto found;
+
+ ret = allocate_free_irq(req_num);
+
+ if (ret < 0)
+ return ret;
+
+found:
+ *out_hwirq = ret + GIC_IRQ_START;
+ return 0;
+}
+
+static 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 = 0, reserved = 0, entry;
+ const __be32 *irqsr;
+ int ret = -ENOMEM;
+
+ cb = kzalloc(sizeof(*cb), GFP_KERNEL);
+
+ if (!cb)
+ return ret;
+
+ cb->crossbar_base = of_iomap(node, 0);
+ if (!cb->crossbar_base)
+ goto err_cb;
+
+ of_property_read_u32(node, "ti,max-crossbar-sources",
+ &cb->max_crossbar_sources);
+ if (!cb->max_crossbar_sources) {
+ pr_err("missing 'ti,max-crossbar-sources' property\n");
+ ret = -EINVAL;
+ goto err_base;
+ }
+
+ of_property_read_u32(node, "ti,max-irqs", &max);
+ if (!max) {
+ pr_err("missing 'ti,max-irqs' property\n");
+ ret = -EINVAL;
+ goto err_base;
+ }
+ cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL);
+ if (!cb->irq_map)
+ goto err_base;
+
+ 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");
+ ret = -EINVAL;
+ goto err_irq_map;
+ }
+ cb->irq_map[entry] = IRQ_RESERVED;
+ }
+ }
+
+ /* Skip irqs hardwired to bypass the crossbar */
+ irqsr = of_get_property(node, "ti,irqs-skip", &size);
+ if (irqsr) {
+ size /= sizeof(__be32);
+
+ for (i = 0; i < size; i++) {
+ of_property_read_u32_index(node,
+ "ti,irqs-skip",
+ i, &entry);
+ if (entry > max) {
+ pr_err("Invalid skip entry\n");
+ ret = -EINVAL;
+ goto err_irq_map;
+ }
+ cb->irq_map[entry] = IRQ_SKIP;
+ }
+ }
+
+
+ cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
+ if (!cb->register_offsets)
+ goto err_irq_map;
+
+ 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");
+ ret = -EINVAL;
+ goto err_reg_offset;
+ 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] == IRQ_RESERVED)
+ continue;
+
+ cb->register_offsets[i] = reserved;
+ reserved += size;
+ }
+
+ of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
+ /* Initialize the crossbar with safe map to start with */
+ for (i = 0; i < max; i++) {
+ if (cb->irq_map[i] == IRQ_RESERVED ||
+ cb->irq_map[i] == IRQ_SKIP)
+ continue;
+
+ cb->write(i, cb->safe_map);
+ }
+
+ register_routable_domain_ops(&routable_irq_domain_ops);
+ return 0;
+
+err_reg_offset:
+ kfree(cb->register_offsets);
+err_irq_map:
+ kfree(cb->irq_map);
+err_base:
+ iounmap(cb->crossbar_base);
+err_cb:
+ kfree(cb);
+
+ cb = NULL;
+ return ret;
+}
+
+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-common.c b/drivers/irqchip/irq-gic-common.c
new file mode 100644
index 000000000000..60ac704d2090
--- /dev/null
+++ b/drivers/irqchip/irq-gic-common.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip/arm-gic.h>
+
+#include "irq-gic-common.h"
+
+void gic_configure_irq(unsigned int irq, unsigned int type,
+ void __iomem *base, void (*sync_access)(void))
+{
+ u32 enablemask = 1 << (irq % 32);
+ u32 enableoff = (irq / 32) * 4;
+ u32 confmask = 0x2 << ((irq % 16) * 2);
+ u32 confoff = (irq / 16) * 4;
+ bool enabled = false;
+ u32 val;
+
+ /*
+ * Read current configuration register, and insert the config
+ * for "irq", depending on "type".
+ */
+ val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
+ if (type == IRQ_TYPE_LEVEL_HIGH)
+ val &= ~confmask;
+ else if (type == IRQ_TYPE_EDGE_RISING)
+ val |= confmask;
+
+ /*
+ * As recommended by the spec, disable the interrupt before changing
+ * the configuration
+ */
+ if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
+ writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
+ if (sync_access)
+ sync_access();
+ enabled = true;
+ }
+
+ /*
+ * Write back the new configuration, and possibly re-enable
+ * the interrupt.
+ */
+ writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+
+ if (enabled)
+ writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+
+ if (sync_access)
+ sync_access();
+}
+
+void __init gic_dist_config(void __iomem *base, int gic_irqs,
+ void (*sync_access)(void))
+{
+ unsigned int i;
+
+ /*
+ * Set all global interrupts to be level triggered, active low.
+ */
+ for (i = 32; i < gic_irqs; i += 16)
+ writel_relaxed(0, base + GIC_DIST_CONFIG + i / 4);
+
+ /*
+ * Set priority on all global interrupts.
+ */
+ for (i = 32; i < gic_irqs; i += 4)
+ writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i);
+
+ /*
+ * Disable all interrupts. Leave the PPI and SGIs alone
+ * as they are enabled by redistributor registers.
+ */
+ for (i = 32; i < gic_irqs; i += 32)
+ writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i / 8);
+
+ if (sync_access)
+ sync_access();
+}
+
+void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
+{
+ int i;
+
+ /*
+ * Deal with the banked PPI and SGI interrupts - disable all
+ * PPI interrupts, ensure all SGI interrupts are enabled.
+ */
+ writel_relaxed(0xffff0000, base + GIC_DIST_ENABLE_CLEAR);
+ writel_relaxed(0x0000ffff, base + GIC_DIST_ENABLE_SET);
+
+ /*
+ * Set priority on PPI and SGI interrupts
+ */
+ for (i = 0; i < 32; i += 4)
+ writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+
+ if (sync_access)
+ sync_access();
+}
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
new file mode 100644
index 000000000000..b41f02481c3a
--- /dev/null
+++ b/drivers/irqchip/irq-gic-common.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _IRQ_GIC_COMMON_H
+#define _IRQ_GIC_COMMON_H
+
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+
+void gic_configure_irq(unsigned int irq, unsigned int type,
+ void __iomem *base, void (*sync_access)(void));
+void gic_dist_config(void __iomem *base, int gic_irqs,
+ void (*sync_access)(void));
+void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
+
+#endif /* _IRQ_GIC_COMMON_H */
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
new file mode 100644
index 000000000000..57eaa5a0b1e3
--- /dev/null
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved.
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/cpu.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+
+#include <linux/irqchip/arm-gic-v3.h>
+
+#include <asm/cputype.h>
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+
+#include "irq-gic-common.h"
+#include "irqchip.h"
+
+struct gic_chip_data {
+ void __iomem *dist_base;
+ void __iomem **redist_base;
+ void __percpu __iomem **rdist;
+ struct irq_domain *domain;
+ u64 redist_stride;
+ u32 redist_regions;
+ unsigned int irq_nr;
+};
+
+static struct gic_chip_data gic_data __read_mostly;
+
+#define gic_data_rdist() (this_cpu_ptr(gic_data.rdist))
+#define gic_data_rdist_rd_base() (*gic_data_rdist())
+#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
+
+/* Our default, arbitrary priority value. Linux only uses one anyway. */
+#define DEFAULT_PMR_VALUE 0xf0
+
+static inline unsigned int gic_irq(struct irq_data *d)
+{
+ return d->hwirq;
+}
+
+static inline int gic_irq_in_rdist(struct irq_data *d)
+{
+ return gic_irq(d) < 32;
+}
+
+static inline void __iomem *gic_dist_base(struct irq_data *d)
+{
+ if (gic_irq_in_rdist(d)) /* SGI+PPI -> SGI_base for this CPU */
+ return gic_data_rdist_sgi_base();
+
+ if (d->hwirq <= 1023) /* SPI -> dist_base */
+ return gic_data.dist_base;
+
+ if (d->hwirq >= 8192)
+ BUG(); /* LPI Detected!!! */
+
+ return NULL;
+}
+
+static void gic_do_wait_for_rwp(void __iomem *base)
+{
+ u32 count = 1000000; /* 1s! */
+
+ while (readl_relaxed(base + GICD_CTLR) & GICD_CTLR_RWP) {
+ count--;
+ if (!count) {
+ pr_err_ratelimited("RWP timeout, gone fishing\n");
+ return;
+ }
+ cpu_relax();
+ udelay(1);
+ };
+}
+
+/* Wait for completion of a distributor change */
+static void gic_dist_wait_for_rwp(void)
+{
+ gic_do_wait_for_rwp(gic_data.dist_base);
+}
+
+/* Wait for completion of a redistributor change */
+static void gic_redist_wait_for_rwp(void)
+{
+ gic_do_wait_for_rwp(gic_data_rdist_rd_base());
+}
+
+/* Low level accessors */
+static u64 gic_read_iar(void)
+{
+ u64 irqstat;
+
+ asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+ return irqstat;
+}
+
+static void gic_write_pmr(u64 val)
+{
+ asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val));
+}
+
+static void gic_write_ctlr(u64 val)
+{
+ asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val));
+ isb();
+}
+
+static void gic_write_grpen1(u64 val)
+{
+ asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val));
+ isb();
+}
+
+static void gic_write_sgi1r(u64 val)
+{
+ asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
+}
+
+static void gic_enable_sre(void)
+{
+ u64 val;
+
+ asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+ val |= ICC_SRE_EL1_SRE;
+ asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val));
+ isb();
+
+ /*
+ * Need to check that the SRE bit has actually been set. If
+ * not, it means that SRE is disabled at EL2. We're going to
+ * die painfully, and there is nothing we can do about it.
+ *
+ * Kindly inform the luser.
+ */
+ asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+ if (!(val & ICC_SRE_EL1_SRE))
+ pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
+}
+
+static void gic_enable_redist(void)
+{
+ void __iomem *rbase;
+ u32 count = 1000000; /* 1s! */
+ u32 val;
+
+ rbase = gic_data_rdist_rd_base();
+
+ /* Wake up this CPU redistributor */
+ val = readl_relaxed(rbase + GICR_WAKER);
+ val &= ~GICR_WAKER_ProcessorSleep;
+ writel_relaxed(val, rbase + GICR_WAKER);
+
+ while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
+ count--;
+ if (!count) {
+ pr_err_ratelimited("redist didn't wake up...\n");
+ return;
+ }
+ cpu_relax();
+ udelay(1);
+ };
+}
+
+/*
+ * Routines to disable, enable, EOI and route interrupts
+ */
+static void gic_poke_irq(struct irq_data *d, u32 offset)
+{
+ u32 mask = 1 << (gic_irq(d) % 32);
+ void (*rwp_wait)(void);
+ void __iomem *base;
+
+ if (gic_irq_in_rdist(d)) {
+ base = gic_data_rdist_sgi_base();
+ rwp_wait = gic_redist_wait_for_rwp;
+ } else {
+ base = gic_data.dist_base;
+ rwp_wait = gic_dist_wait_for_rwp;
+ }
+
+ writel_relaxed(mask, base + offset + (gic_irq(d) / 32) * 4);
+ rwp_wait();
+}
+
+static int gic_peek_irq(struct irq_data *d, u32 offset)
+{
+ u32 mask = 1 << (gic_irq(d) % 32);
+ void __iomem *base;
+
+ if (gic_irq_in_rdist(d))
+ base = gic_data_rdist_sgi_base();
+ else
+ base = gic_data.dist_base;
+
+ return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
+}
+
+static void gic_mask_irq(struct irq_data *d)
+{
+ gic_poke_irq(d, GICD_ICENABLER);
+}
+
+static void gic_unmask_irq(struct irq_data *d)
+{
+ gic_poke_irq(d, GICD_ISENABLER);
+}
+
+static void gic_eoi_irq(struct irq_data *d)
+{
+ gic_write_eoir(gic_irq(d));
+}
+
+static int gic_set_type(struct irq_data *d, unsigned int type)
+{
+ unsigned int irq = gic_irq(d);
+ void (*rwp_wait)(void);
+ void __iomem *base;
+
+ /* Interrupt configuration for SGIs can't be changed */
+ if (irq < 16)
+ return -EINVAL;
+
+ if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+ return -EINVAL;
+
+ if (gic_irq_in_rdist(d)) {
+ base = gic_data_rdist_sgi_base();
+ rwp_wait = gic_redist_wait_for_rwp;
+ } else {
+ base = gic_data.dist_base;
+ rwp_wait = gic_dist_wait_for_rwp;
+ }
+
+ gic_configure_irq(irq, type, base, rwp_wait);
+
+ return 0;
+}
+
+static u64 gic_mpidr_to_affinity(u64 mpidr)
+{
+ u64 aff;
+
+ aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 0));
+
+ return aff;
+}
+
+static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
+{
+ u64 irqnr;
+
+ do {
+ irqnr = gic_read_iar();
+
+ if (likely(irqnr > 15 && irqnr < 1020)) {
+ u64 irq = irq_find_mapping(gic_data.domain, irqnr);
+ if (likely(irq)) {
+ handle_IRQ(irq, regs);
+ continue;
+ }
+
+ WARN_ONCE(true, "Unexpected SPI received!\n");
+ gic_write_eoir(irqnr);
+ }
+ if (irqnr < 16) {
+ gic_write_eoir(irqnr);
+#ifdef CONFIG_SMP
+ handle_IPI(irqnr, regs);
+#else
+ WARN_ONCE(true, "Unexpected SGI received!\n");
+#endif
+ continue;
+ }
+ } while (irqnr != ICC_IAR1_EL1_SPURIOUS);
+}
+
+static void __init gic_dist_init(void)
+{
+ unsigned int i;
+ u64 affinity;
+ void __iomem *base = gic_data.dist_base;
+
+ /* Disable the distributor */
+ writel_relaxed(0, base + GICD_CTLR);
+ gic_dist_wait_for_rwp();
+
+ gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);
+
+ /* Enable distributor with ARE, Group1 */
+ writel_relaxed(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
+ base + GICD_CTLR);
+
+ /*
+ * Set all global interrupts to the boot CPU only. ARE must be
+ * enabled.
+ */
+ affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
+ for (i = 32; i < gic_data.irq_nr; i++)
+ writeq_relaxed(affinity, base + GICD_IROUTER + i * 8);
+}
+
+static int gic_populate_rdist(void)
+{
+ u64 mpidr = cpu_logical_map(smp_processor_id());
+ u64 typer;
+ u32 aff;
+ int i;
+
+ /*
+ * Convert affinity to a 32bit value that can be matched to
+ * GICR_TYPER bits [63:32].
+ */
+ aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 24 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 0));
+
+ for (i = 0; i < gic_data.redist_regions; i++) {
+ void __iomem *ptr = gic_data.redist_base[i];
+ u32 reg;
+
+ reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK;
+ if (reg != GIC_PIDR2_ARCH_GICv3 &&
+ reg != GIC_PIDR2_ARCH_GICv4) { /* We're in trouble... */
+ pr_warn("No redistributor present @%p\n", ptr);
+ break;
+ }
+
+ do {
+ typer = readq_relaxed(ptr + GICR_TYPER);
+ if ((typer >> 32) == aff) {
+ gic_data_rdist_rd_base() = ptr;
+ pr_info("CPU%d: found redistributor %llx @%p\n",
+ smp_processor_id(),
+ (unsigned long long)mpidr, ptr);
+ return 0;
+ }
+
+ if (gic_data.redist_stride) {
+ ptr += gic_data.redist_stride;
+ } else {
+ ptr += SZ_64K * 2; /* Skip RD_base + SGI_base */
+ if (typer & GICR_TYPER_VLPIS)
+ ptr += SZ_64K * 2; /* Skip VLPI_base + reserved page */
+ }
+ } while (!(typer & GICR_TYPER_LAST));
+ }
+
+ /* We couldn't even deal with ourselves... */
+ WARN(true, "CPU%d: mpidr %llx has no re-distributor!\n",
+ smp_processor_id(), (unsigned long long)mpidr);
+ return -ENODEV;
+}
+
+static void gic_cpu_init(void)
+{
+ void __iomem *rbase;
+
+ /* Register ourselves with the rest of the world */
+ if (gic_populate_rdist())
+ return;
+
+ gic_enable_redist();
+
+ rbase = gic_data_rdist_sgi_base();
+
+ gic_cpu_config(rbase, gic_redist_wait_for_rwp);
+
+ /* Enable system registers */
+ gic_enable_sre();
+
+ /* Set priority mask register */
+ gic_write_pmr(DEFAULT_PMR_VALUE);
+
+ /* EOI deactivates interrupt too (mode 0) */
+ gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+
+ /* ... and let's hit the road... */
+ gic_write_grpen1(1);
+}
+
+#ifdef CONFIG_SMP
+static int gic_secondary_init(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+ gic_cpu_init();
+ return NOTIFY_OK;
+}
+
+/*
+ * Notifier for enabling the GIC CPU interface. Set an arbitrarily high
+ * priority because the GIC needs to be up before the ARM generic timers.
+ */
+static struct notifier_block gic_cpu_notifier = {
+ .notifier_call = gic_secondary_init,
+ .priority = 100,
+};
+
+static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
+ u64 cluster_id)
+{
+ int cpu = *base_cpu;
+ u64 mpidr = cpu_logical_map(cpu);
+ u16 tlist = 0;
+
+ while (cpu < nr_cpu_ids) {
+ /*
+ * If we ever get a cluster of more than 16 CPUs, just
+ * scream and skip that CPU.
+ */
+ if (WARN_ON((mpidr & 0xff) >= 16))
+ goto out;
+
+ tlist |= 1 << (mpidr & 0xf);
+
+ cpu = cpumask_next(cpu, mask);
+ if (cpu == nr_cpu_ids)
+ goto out;
+
+ mpidr = cpu_logical_map(cpu);
+
+ if (cluster_id != (mpidr & ~0xffUL)) {
+ cpu--;
+ goto out;
+ }
+ }
+out:
+ *base_cpu = cpu;
+ return tlist;
+}
+
+static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
+{
+ u64 val;
+
+ val = (MPIDR_AFFINITY_LEVEL(cluster_id, 3) << 48 |
+ MPIDR_AFFINITY_LEVEL(cluster_id, 2) << 32 |
+ irq << 24 |
+ MPIDR_AFFINITY_LEVEL(cluster_id, 1) << 16 |
+ tlist);
+
+ pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
+ gic_write_sgi1r(val);
+}
+
+static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
+{
+ int cpu;
+
+ if (WARN_ON(irq >= 16))
+ return;
+
+ /*
+ * Ensure that stores to Normal memory are visible to the
+ * other CPUs before issuing the IPI.
+ */
+ smp_wmb();
+
+ for_each_cpu_mask(cpu, *mask) {
+ u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL;
+ u16 tlist;
+
+ tlist = gic_compute_target_list(&cpu, mask, cluster_id);
+ gic_send_sgi(cluster_id, tlist, irq);
+ }
+
+ /* Force the above writes to ICC_SGI1R_EL1 to be executed */
+ isb();
+}
+
+static void gic_smp_init(void)
+{
+ set_smp_cross_call(gic_raise_softirq);
+ register_cpu_notifier(&gic_cpu_notifier);
+}
+
+static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
+ bool force)
+{
+ unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask);
+ void __iomem *reg;
+ int enabled;
+ u64 val;
+
+ if (gic_irq_in_rdist(d))
+ return -EINVAL;
+
+ /* If interrupt was enabled, disable it first */
+ enabled = gic_peek_irq(d, GICD_ISENABLER);
+ if (enabled)
+ gic_mask_irq(d);
+
+ reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);
+ val = gic_mpidr_to_affinity(cpu_logical_map(cpu));
+
+ writeq_relaxed(val, reg);
+
+ /*
+ * If the interrupt was enabled, enabled it again. Otherwise,
+ * just wait for the distributor to have digested our changes.
+ */
+ if (enabled)
+ gic_unmask_irq(d);
+ else
+ gic_dist_wait_for_rwp();
+
+ return IRQ_SET_MASK_OK;
+}
+#else
+#define gic_set_affinity NULL
+#define gic_smp_init() do { } while(0)
+#endif
+
+static struct irq_chip gic_chip = {
+ .name = "GICv3",
+ .irq_mask = gic_mask_irq,
+ .irq_unmask = gic_unmask_irq,
+ .irq_eoi = gic_eoi_irq,
+ .irq_set_type = gic_set_type,
+ .irq_set_affinity = gic_set_affinity,
+};
+
+static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ /* SGIs are private to the core kernel */
+ if (hw < 16)
+ return -EPERM;
+ /* PPIs */
+ if (hw < 32) {
+ irq_set_percpu_devid(irq);
+ irq_set_chip_and_handler(irq, &gic_chip,
+ handle_percpu_devid_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+ }
+ /* SPIs */
+ if (hw >= 32 && hw < gic_data.irq_nr) {
+ irq_set_chip_and_handler(irq, &gic_chip,
+ handle_fasteoi_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ irq_set_chip_data(irq, d->host_data);
+ return 0;
+}
+
+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)
+{
+ if (d->of_node != controller)
+ return -EINVAL;
+ if (intsize < 3)
+ return -EINVAL;
+
+ switch(intspec[0]) {
+ case 0: /* SPI */
+ *out_hwirq = intspec[1] + 32;
+ break;
+ case 1: /* PPI */
+ *out_hwirq = intspec[1] + 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+}
+
+static const struct irq_domain_ops gic_irq_domain_ops = {
+ .map = gic_irq_domain_map,
+ .xlate = gic_irq_domain_xlate,
+};
+
+static int __init gic_of_init(struct device_node *node, struct device_node *parent)
+{
+ void __iomem *dist_base;
+ void __iomem **redist_base;
+ u64 redist_stride;
+ u32 redist_regions;
+ u32 reg;
+ int gic_irqs;
+ int err;
+ int i;
+
+ dist_base = of_iomap(node, 0);
+ if (!dist_base) {
+ pr_err("%s: unable to map gic dist registers\n",
+ node->full_name);
+ return -ENXIO;
+ }
+
+ reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+ if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4) {
+ pr_err("%s: no distributor detected, giving up\n",
+ node->full_name);
+ err = -ENODEV;
+ goto out_unmap_dist;
+ }
+
+ if (of_property_read_u32(node, "#redistributor-regions", &redist_regions))
+ redist_regions = 1;
+
+ redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL);
+ if (!redist_base) {
+ err = -ENOMEM;
+ goto out_unmap_dist;
+ }
+
+ for (i = 0; i < redist_regions; i++) {
+ redist_base[i] = of_iomap(node, 1 + i);
+ if (!redist_base[i]) {
+ pr_err("%s: couldn't map region %d\n",
+ node->full_name, i);
+ err = -ENODEV;
+ goto out_unmap_rdist;
+ }
+ }
+
+ if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
+ redist_stride = 0;
+
+ gic_data.dist_base = dist_base;
+ gic_data.redist_base = redist_base;
+ gic_data.redist_regions = redist_regions;
+ gic_data.redist_stride = redist_stride;
+
+ /*
+ * Find out how many interrupts are supported.
+ * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
+ */
+ gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f;
+ gic_irqs = (gic_irqs + 1) * 32;
+ if (gic_irqs > 1020)
+ gic_irqs = 1020;
+ gic_data.irq_nr = gic_irqs;
+
+ gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
+ &gic_data);
+ gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist));
+
+ if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ set_handle_irq(gic_handle_irq);
+
+ gic_smp_init();
+ gic_dist_init();
+ gic_cpu_init();
+
+ return 0;
+
+out_free:
+ if (gic_data.domain)
+ irq_domain_remove(gic_data.domain);
+ free_percpu(gic_data.rdist);
+out_unmap_rdist:
+ for (i = 0; i < redist_regions; i++)
+ if (redist_base[i])
+ iounmap(redist_base[i]);
+ kfree(redist_base);
+out_unmap_dist:
+ iounmap(dist_base);
+ return err;
+}
+
+IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 3b7650fccdc0..4b959e606fe8 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -40,10 +40,12 @@
#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>
+#include "irq-gic-common.h"
#include "irqchip.h"
union gic_base {
@@ -186,12 +188,6 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
- u32 enablemask = 1 << (gicirq % 32);
- u32 enableoff = (gicirq / 32) * 4;
- u32 confmask = 0x2 << ((gicirq % 16) * 2);
- u32 confoff = (gicirq / 16) * 4;
- bool enabled = false;
- u32 val;
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
@@ -205,25 +201,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
if (gic_arch_extn.irq_set_type)
gic_arch_extn.irq_set_type(d, type);
- val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
- if (type == IRQ_TYPE_LEVEL_HIGH)
- val &= ~confmask;
- else if (type == IRQ_TYPE_EDGE_RISING)
- val |= confmask;
-
- /*
- * As recommended by the spec, disable the interrupt before changing
- * the configuration
- */
- if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
- enabled = true;
- }
-
- writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
-
- if (enabled)
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+ gic_configure_irq(gicirq, type, base, NULL);
raw_spin_unlock(&irq_controller_lock);
@@ -244,10 +222,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;
@@ -285,7 +267,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);
@@ -381,12 +363,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
writel_relaxed(0, base + GIC_DIST_CTRL);
/*
- * Set all global interrupts to be level triggered, active low.
- */
- for (i = 32; i < gic_irqs; i += 16)
- writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);
-
- /*
* Set all global interrupts to this CPU only.
*/
cpumask = gic_get_cpumask(gic);
@@ -395,18 +371,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
- /*
- * Set priority on all global interrupts.
- */
- for (i = 32; i < gic_irqs; i += 4)
- writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
-
- /*
- * Disable all interrupts. Leave the PPI and SGIs alone
- * as these enables are banked registers.
- */
- for (i = 32; i < gic_irqs; i += 32)
- writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
+ gic_dist_config(base, gic_irqs, NULL);
writel_relaxed(1, base + GIC_DIST_CTRL);
}
@@ -433,18 +398,7 @@ static void gic_cpu_init(struct gic_chip_data *gic)
if (i != cpu)
gic_cpu_map[i] &= ~cpu_mask;
- /*
- * Deal with the banked PPI and SGI interrupts - disable all
- * PPI interrupts, ensure all SGI interrupts are enabled.
- */
- writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
- writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
-
- /*
- * Set priority on PPI and SGI interrupts
- */
- for (i = 0; i < 32; i += 4)
- writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
+ gic_cpu_config(dist_base, NULL);
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
writel_relaxed(1, base + GIC_CPU_CTRL);
@@ -659,9 +613,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);
@@ -822,16 +776,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)
@@ -841,11 +804,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
@@ -869,9 +841,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)
@@ -879,6 +883,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);
@@ -897,7 +902,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;
}
@@ -944,14 +951,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;
@@ -1003,8 +1021,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 3c8827fe83f3..1c3e2c9b46ba 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 63b3d4eb0ef7..4044ff287663 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-nvic.c b/drivers/irqchip/irq-nvic.c
index 70bdf6edb7bb..4ff0805fca01 100644
--- a/drivers/irqchip/irq-nvic.c
+++ b/drivers/irqchip/irq-nvic.c
@@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
handle_IRQ(irq, regs);
}
-static void nvic_eoi(struct irq_data *d)
-{
- /*
- * This is a no-op as end of interrupt is signaled by the exception
- * return sequence.
- */
-}
-
static int __init nvic_of_init(struct device_node *node,
struct device_node *parent)
{
@@ -102,7 +94,10 @@ static int __init nvic_of_init(struct device_node *node,
gc->chip_types[0].regs.disable = NVIC_ICER;
gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
- gc->chip_types[0].chip.irq_eoi = nvic_eoi;
+ /* This is a no-op as end of interrupt is signaled by the
+ * exception return sequence.
+ */
+ gc->chip_types[0].chip.irq_eoi = irq_gc_noop;
/* disable interrupts */
writel_relaxed(~0, gc->reg_base + NVIC_ICER);
diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c
new file mode 100644
index 000000000000..17ff033d9925
--- /dev/null
+++ b/drivers/irqchip/irq-or1k-pic.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
+ * Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi>
+ *
+ * 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/irq.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include "irqchip.h"
+
+/* OR1K PIC implementation */
+
+struct or1k_pic_dev {
+ struct irq_chip chip;
+ irq_flow_handler_t handle;
+ unsigned long flags;
+};
+
+/*
+ * We're a couple of cycles faster than the generic implementations with
+ * these 'fast' versions.
+ */
+
+static void or1k_pic_mask(struct irq_data *data)
+{
+ mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
+}
+
+static void or1k_pic_unmask(struct irq_data *data)
+{
+ mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq));
+}
+
+static void or1k_pic_ack(struct irq_data *data)
+{
+ mtspr(SPR_PICSR, (1UL << data->hwirq));
+}
+
+static void or1k_pic_mask_ack(struct irq_data *data)
+{
+ mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
+ mtspr(SPR_PICSR, (1UL << data->hwirq));
+}
+
+/*
+ * There are two oddities with the OR1200 PIC implementation:
+ * i) LEVEL-triggered interrupts are latched and need to be cleared
+ * ii) the interrupt latch is cleared by writing a 0 to the bit,
+ * as opposed to a 1 as mandated by the spec
+ */
+static void or1k_pic_or1200_ack(struct irq_data *data)
+{
+ mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
+}
+
+static void or1k_pic_or1200_mask_ack(struct irq_data *data)
+{
+ mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
+ mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
+}
+
+static struct or1k_pic_dev or1k_pic_level = {
+ .chip = {
+ .name = "or1k-PIC-level",
+ .irq_unmask = or1k_pic_unmask,
+ .irq_mask = or1k_pic_mask,
+ .irq_mask_ack = or1k_pic_mask,
+ },
+ .handle = handle_level_irq,
+ .flags = IRQ_LEVEL | IRQ_NOPROBE,
+};
+
+static struct or1k_pic_dev or1k_pic_edge = {
+ .chip = {
+ .name = "or1k-PIC-edge",
+ .irq_unmask = or1k_pic_unmask,
+ .irq_mask = or1k_pic_mask,
+ .irq_ack = or1k_pic_ack,
+ .irq_mask_ack = or1k_pic_mask_ack,
+ },
+ .handle = handle_edge_irq,
+ .flags = IRQ_LEVEL | IRQ_NOPROBE,
+};
+
+static struct or1k_pic_dev or1k_pic_or1200 = {
+ .chip = {
+ .name = "or1200-PIC",
+ .irq_unmask = or1k_pic_unmask,
+ .irq_mask = or1k_pic_mask,
+ .irq_ack = or1k_pic_or1200_ack,
+ .irq_mask_ack = or1k_pic_or1200_mask_ack,
+ },
+ .handle = handle_level_irq,
+ .flags = IRQ_LEVEL | IRQ_NOPROBE,
+};
+
+static struct irq_domain *root_domain;
+
+static inline int pic_get_irq(int first)
+{
+ int hwirq;
+
+ hwirq = ffs(mfspr(SPR_PICSR) >> first);
+ if (!hwirq)
+ return NO_IRQ;
+ else
+ hwirq = hwirq + first - 1;
+
+ return irq_find_mapping(root_domain, hwirq);
+}
+
+static void or1k_pic_handle_irq(struct pt_regs *regs)
+{
+ int irq = -1;
+
+ while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
+ handle_IRQ(irq, regs);
+}
+
+static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ struct or1k_pic_dev *pic = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pic->chip, pic->handle);
+ irq_set_status_flags(irq, pic->flags);
+
+ return 0;
+}
+
+static const struct irq_domain_ops or1k_irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = or1k_map,
+};
+
+/*
+ * This sets up the IRQ domain for the PIC built in to the OpenRISC
+ * 1000 CPU. This is the "root" domain as these are the interrupts
+ * that directly trigger an exception in the CPU.
+ */
+static int __init or1k_pic_init(struct device_node *node,
+ struct or1k_pic_dev *pic)
+{
+ /* Disable all interrupts until explicitly requested */
+ mtspr(SPR_PICMR, (0UL));
+
+ root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops,
+ pic);
+
+ set_handle_irq(or1k_pic_handle_irq);
+
+ return 0;
+}
+
+static int __init or1k_pic_or1200_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return or1k_pic_init(node, &or1k_pic_or1200);
+}
+IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init);
+IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init);
+
+static int __init or1k_pic_level_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return or1k_pic_init(node, &or1k_pic_level);
+}
+IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level",
+ or1k_pic_level_init);
+
+static int __init or1k_pic_edge_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return or1k_pic_init(node, &or1k_pic_edge);
+}
+IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init);
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c
index e25f246cd2fb..34d18b48bb78 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 bbcc944ed94f..78a6accd205f 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 581eefe331ae..5e54f6d71e77 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 473f09a74d4d..7d35287f9e90 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 cad3e2495552..0fe2f718d81c 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 e445ba2d6add..0f6486d4f1b0 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 8527743b5cef..9c145a7cb056 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
@@ -19,7 +19,6 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
-#include <linux/irqchip/spear-shirq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -27,20 +26,73 @@
#include "irqchip.h"
-static DEFINE_SPINLOCK(lock);
+/*
+ * struct spear_shirq: shared irq structure
+ *
+ * base: Base register address
+ * status_reg: Status register offset for chained interrupt handler
+ * mask_reg: Mask register offset for irq chip
+ * mask: Mask to apply to the status register
+ * virq_base: Base virtual interrupt number
+ * nr_irqs: Number of interrupts handled by this block
+ * offset: Bit offset of the first interrupt
+ * irq_chip: Interrupt controller chip used for this instance,
+ * if NULL group is disabled, but accounted
+ */
+struct spear_shirq {
+ void __iomem *base;
+ u32 status_reg;
+ u32 mask_reg;
+ u32 mask;
+ u32 virq_base;
+ u32 nr_irqs;
+ u32 offset;
+ struct irq_chip *irq_chip;
+};
/* spear300 shared irq registers offsets and masks */
#define SPEAR300_INT_ENB_MASK_REG 0x54
#define SPEAR300_INT_STS_MASK_REG 0x58
+static DEFINE_RAW_SPINLOCK(shirq_lock);
+
+static void shirq_irq_mask(struct irq_data *d)
+{
+ struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
+ u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
+ u32 __iomem *reg = shirq->base + shirq->mask_reg;
+
+ raw_spin_lock(&shirq_lock);
+ val = readl(reg) & ~(0x1 << shift);
+ writel(val, reg);
+ raw_spin_unlock(&shirq_lock);
+}
+
+static void shirq_irq_unmask(struct irq_data *d)
+{
+ struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
+ u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
+ u32 __iomem *reg = shirq->base + shirq->mask_reg;
+
+ raw_spin_lock(&shirq_lock);
+ val = readl(reg) | (0x1 << shift);
+ writel(val, reg);
+ raw_spin_unlock(&shirq_lock);
+}
+
+static struct irq_chip shirq_chip = {
+ .name = "spear-shirq",
+ .irq_mask = shirq_irq_mask,
+ .irq_unmask = shirq_irq_unmask,
+};
+
static struct spear_shirq spear300_shirq_ras1 = {
- .irq_nr = 9,
- .irq_bit_off = 0,
- .regs = {
- .enb_reg = SPEAR300_INT_ENB_MASK_REG,
- .status_reg = SPEAR300_INT_STS_MASK_REG,
- .clear_reg = -1,
- },
+ .offset = 0,
+ .nr_irqs = 9,
+ .mask = ((0x1 << 9) - 1) << 0,
+ .irq_chip = &shirq_chip,
+ .status_reg = SPEAR300_INT_STS_MASK_REG,
+ .mask_reg = SPEAR300_INT_ENB_MASK_REG,
};
static struct spear_shirq *spear300_shirq_blocks[] = {
@@ -51,43 +103,35 @@ static struct spear_shirq *spear300_shirq_blocks[] = {
#define SPEAR310_INT_STS_MASK_REG 0x04
static struct spear_shirq spear310_shirq_ras1 = {
- .irq_nr = 8,
- .irq_bit_off = 0,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR310_INT_STS_MASK_REG,
- .clear_reg = -1,
- },
+ .offset = 0,
+ .nr_irqs = 8,
+ .mask = ((0x1 << 8) - 1) << 0,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
};
static struct spear_shirq spear310_shirq_ras2 = {
- .irq_nr = 5,
- .irq_bit_off = 8,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR310_INT_STS_MASK_REG,
- .clear_reg = -1,
- },
+ .offset = 8,
+ .nr_irqs = 5,
+ .mask = ((0x1 << 5) - 1) << 8,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
};
static struct spear_shirq spear310_shirq_ras3 = {
- .irq_nr = 1,
- .irq_bit_off = 13,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR310_INT_STS_MASK_REG,
- .clear_reg = -1,
- },
+ .offset = 13,
+ .nr_irqs = 1,
+ .mask = ((0x1 << 1) - 1) << 13,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
};
static struct spear_shirq spear310_shirq_intrcomm_ras = {
- .irq_nr = 3,
- .irq_bit_off = 14,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR310_INT_STS_MASK_REG,
- .clear_reg = -1,
- },
+ .offset = 14,
+ .nr_irqs = 3,
+ .mask = ((0x1 << 3) - 1) << 14,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
};
static struct spear_shirq *spear310_shirq_blocks[] = {
@@ -102,50 +146,34 @@ static struct spear_shirq *spear310_shirq_blocks[] = {
#define SPEAR320_INT_CLR_MASK_REG 0x04
#define SPEAR320_INT_ENB_MASK_REG 0x08
-static struct spear_shirq spear320_shirq_ras1 = {
- .irq_nr = 3,
- .irq_bit_off = 7,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR320_INT_STS_MASK_REG,
- .clear_reg = SPEAR320_INT_CLR_MASK_REG,
- .reset_to_clear = 1,
- },
+static struct spear_shirq spear320_shirq_ras3 = {
+ .offset = 0,
+ .nr_irqs = 7,
+ .mask = ((0x1 << 7) - 1) << 0,
};
-static struct spear_shirq spear320_shirq_ras2 = {
- .irq_nr = 1,
- .irq_bit_off = 10,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR320_INT_STS_MASK_REG,
- .clear_reg = SPEAR320_INT_CLR_MASK_REG,
- .reset_to_clear = 1,
- },
+static struct spear_shirq spear320_shirq_ras1 = {
+ .offset = 7,
+ .nr_irqs = 3,
+ .mask = ((0x1 << 3) - 1) << 7,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
};
-static struct spear_shirq spear320_shirq_ras3 = {
- .irq_nr = 3,
- .irq_bit_off = 0,
- .invalid_irq = 1,
- .regs = {
- .enb_reg = SPEAR320_INT_ENB_MASK_REG,
- .reset_to_enb = 1,
- .status_reg = SPEAR320_INT_STS_MASK_REG,
- .clear_reg = SPEAR320_INT_CLR_MASK_REG,
- .reset_to_clear = 1,
- },
+static struct spear_shirq spear320_shirq_ras2 = {
+ .offset = 10,
+ .nr_irqs = 1,
+ .mask = ((0x1 << 1) - 1) << 10,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
};
static struct spear_shirq spear320_shirq_intrcomm_ras = {
- .irq_nr = 11,
- .irq_bit_off = 11,
- .regs = {
- .enb_reg = -1,
- .status_reg = SPEAR320_INT_STS_MASK_REG,
- .clear_reg = SPEAR320_INT_CLR_MASK_REG,
- .reset_to_clear = 1,
- },
+ .offset = 11,
+ .nr_irqs = 11,
+ .mask = ((0x1 << 11) - 1) << 11,
+ .irq_chip = &dummy_irq_chip,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
};
static struct spear_shirq *spear320_shirq_blocks[] = {
@@ -155,104 +183,46 @@ static struct spear_shirq *spear320_shirq_blocks[] = {
&spear320_shirq_intrcomm_ras,
};
-static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
-{
- struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
- u32 val, offset = d->irq - shirq->irq_base;
- unsigned long flags;
-
- if (shirq->regs.enb_reg == -1)
- return;
-
- spin_lock_irqsave(&lock, flags);
- val = readl(shirq->base + shirq->regs.enb_reg);
-
- if (mask ^ shirq->regs.reset_to_enb)
- val &= ~(0x1 << shirq->irq_bit_off << offset);
- else
- val |= 0x1 << shirq->irq_bit_off << offset;
-
- writel(val, shirq->base + shirq->regs.enb_reg);
- spin_unlock_irqrestore(&lock, flags);
-
-}
-
-static void shirq_irq_mask(struct irq_data *d)
-{
- shirq_irq_mask_unmask(d, 1);
-}
-
-static void shirq_irq_unmask(struct irq_data *d)
-{
- shirq_irq_mask_unmask(d, 0);
-}
-
-static struct irq_chip shirq_chip = {
- .name = "spear-shirq",
- .irq_ack = shirq_irq_mask,
- .irq_mask = shirq_irq_mask,
- .irq_unmask = shirq_irq_unmask,
-};
-
static void shirq_handler(unsigned irq, struct irq_desc *desc)
{
- u32 i, j, val, mask, tmp;
- struct irq_chip *chip;
struct spear_shirq *shirq = irq_get_handler_data(irq);
+ u32 pend;
- chip = irq_get_chip(irq);
- chip->irq_ack(&desc->irq_data);
-
- mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
- while ((val = readl(shirq->base + shirq->regs.status_reg) &
- mask)) {
-
- val >>= shirq->irq_bit_off;
- for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {
-
- if (!(j & val))
- continue;
+ pend = readl(shirq->base + shirq->status_reg) & shirq->mask;
+ pend >>= shirq->offset;
- generic_handle_irq(shirq->irq_base + i);
+ while (pend) {
+ int irq = __ffs(pend);
- /* clear interrupt */
- if (shirq->regs.clear_reg == -1)
- continue;
-
- tmp = readl(shirq->base + shirq->regs.clear_reg);
- if (shirq->regs.reset_to_clear)
- tmp &= ~(j << shirq->irq_bit_off);
- else
- tmp |= (j << shirq->irq_bit_off);
- writel(tmp, shirq->base + shirq->regs.clear_reg);
- }
+ pend &= ~(0x1 << irq);
+ generic_handle_irq(shirq->virq_base + irq);
}
- chip->irq_unmask(&desc->irq_data);
}
-static void __init spear_shirq_register(struct spear_shirq *shirq)
+static void __init spear_shirq_register(struct spear_shirq *shirq,
+ int parent_irq)
{
int i;
- if (shirq->invalid_irq)
+ if (!shirq->irq_chip)
return;
- irq_set_chained_handler(shirq->irq, shirq_handler);
- for (i = 0; i < shirq->irq_nr; i++) {
- irq_set_chip_and_handler(shirq->irq_base + i,
- &shirq_chip, handle_simple_irq);
- set_irq_flags(shirq->irq_base + i, IRQF_VALID);
- irq_set_chip_data(shirq->irq_base + i, shirq);
- }
+ irq_set_chained_handler(parent_irq, shirq_handler);
+ irq_set_handler_data(parent_irq, shirq);
- irq_set_handler_data(shirq->irq, shirq);
+ for (i = 0; i < shirq->nr_irqs; i++) {
+ irq_set_chip_and_handler(shirq->virq_base + i,
+ shirq->irq_chip, handle_simple_irq);
+ set_irq_flags(shirq->virq_base + i, IRQF_VALID);
+ irq_set_chip_data(shirq->virq_base + i, shirq);
+ }
}
static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
struct device_node *np)
{
- int i, irq_base, hwirq = 0, irq_nr = 0;
- static struct irq_domain *shirq_domain;
+ int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0;
+ struct irq_domain *shirq_domain;
void __iomem *base;
base = of_iomap(np, 0);
@@ -262,15 +232,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
}
for (i = 0; i < block_nr; i++)
- irq_nr += shirq_blocks[i]->irq_nr;
+ nr_irqs += shirq_blocks[i]->nr_irqs;
- irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
- if (IS_ERR_VALUE(irq_base)) {
+ virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+ if (IS_ERR_VALUE(virq_base)) {
pr_err("%s: irq desc alloc failed\n", __func__);
goto err_unmap;
}
- shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
+ shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0,
&irq_domain_simple_ops, NULL);
if (WARN_ON(!shirq_domain)) {
pr_warn("%s: irq domain init failed\n", __func__);
@@ -279,41 +249,41 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
for (i = 0; i < block_nr; i++) {
shirq_blocks[i]->base = base;
- shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
+ shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain,
hwirq);
- shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
- spear_shirq_register(shirq_blocks[i]);
- hwirq += shirq_blocks[i]->irq_nr;
+ parent_irq = irq_of_parse_and_map(np, i);
+ spear_shirq_register(shirq_blocks[i], parent_irq);
+ hwirq += shirq_blocks[i]->nr_irqs;
}
return 0;
err_free_desc:
- irq_free_descs(irq_base, irq_nr);
+ irq_free_descs(virq_base, nr_irqs);
err_unmap:
iounmap(base);
return -ENXIO;
}
-int __init spear300_shirq_of_init(struct device_node *np,
- struct device_node *parent)
+static int __init spear300_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
{
return shirq_init(spear300_shirq_blocks,
ARRAY_SIZE(spear300_shirq_blocks), np);
}
IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
-int __init spear310_shirq_of_init(struct device_node *np,
- struct device_node *parent)
+static int __init spear310_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
{
return shirq_init(spear310_shirq_blocks,
ARRAY_SIZE(spear310_shirq_blocks), np);
}
IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
-int __init spear320_shirq_of_init(struct device_node *np,
- struct device_node *parent)
+static int __init spear320_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
{
return shirq_init(spear320_shirq_blocks,
ARRAY_SIZE(spear320_shirq_blocks), np);