summaryrefslogtreecommitdiff
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig6
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/alphascale_asm9260-icoll.h109
-rw-r--r--drivers/irqchip/exynos-combiner.c2
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c14
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c2
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c62
-rw-r--r--drivers/irqchip/irq-crossbar.c62
-rw-r--r--drivers/irqchip/irq-gic-common.c24
-rw-r--r--drivers/irqchip/irq-gic-common.h9
-rw-r--r--drivers/irqchip/irq-gic-v2m.c163
-rw-r--r--drivers/irqchip/irq-gic-v3-its-pci-msi.c7
-rw-r--r--drivers/irqchip/irq-gic-v3-its-platform-msi.c21
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c83
-rw-r--r--drivers/irqchip/irq-gic-v3.c161
-rw-r--r--drivers/irqchip/irq-gic.c217
-rw-r--r--drivers/irqchip/irq-hip04.c2
-rw-r--r--drivers/irqchip/irq-i8259.c4
-rw-r--r--drivers/irqchip/irq-imx-gpcv2.c64
-rw-r--r--drivers/irqchip/irq-mips-gic.c14
-rw-r--r--drivers/irqchip/irq-mtk-sysirq.c49
-rw-r--r--drivers/irqchip/irq-mxs.c171
-rw-r--r--drivers/irqchip/irq-nvic.c18
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c6
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c86
-rw-r--r--drivers/irqchip/irq-s3c24xx.c4
-rw-r--r--drivers/irqchip/irq-sunxi-nmi.c22
-rw-r--r--drivers/irqchip/irq-tegra.c55
-rw-r--r--drivers/irqchip/irq-versatile-fpga.c5
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c45
-rw-r--r--drivers/irqchip/irqchip.c5
31 files changed, 977 insertions, 517 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 27b52c872..4d7294e5d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -123,6 +123,7 @@ config RENESAS_INTC_IRQPIN
config RENESAS_IRQC
bool
+ select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
config ST_IRQCHIP
@@ -187,3 +188,8 @@ config IMX_GPCV2
select IRQ_DOMAIN
help
Enables the wakeup IRQs for IMX platforms with GPCv2 block
+
+config IRQ_MXS
+ def_bool y if MACH_ASM9260 || ARCH_MXS
+ select IRQ_DOMAIN
+ select STMP_DEVICE
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index bb3048f00..177f78f6e 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
-obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
+obj-$(CONFIG_IRQ_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
diff --git a/drivers/irqchip/alphascale_asm9260-icoll.h b/drivers/irqchip/alphascale_asm9260-icoll.h
new file mode 100644
index 000000000..5cec108ee
--- /dev/null
+++ b/drivers/irqchip/alphascale_asm9260-icoll.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * 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.
+ */
+
+#ifndef _ALPHASCALE_ASM9260_ICOLL_H
+#define _ALPHASCALE_ASM9260_ICOLL_H
+
+#define ASM9260_NUM_IRQS 64
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+
+#define ASM9260_HW_ICOLL_VECTOR 0x0000
+/*
+ * bits 31:2
+ * This register presents the vector address for the interrupt currently
+ * active on the CPU IRQ input. Writing to this register notifies the
+ * interrupt collector that the interrupt service routine for the current
+ * interrupt has been entered.
+ * The exception trap should have a LDPC instruction from this address:
+ * LDPC ASM9260_HW_ICOLL_VECTOR_ADDR; IRQ exception at 0xffff0018
+ */
+
+/*
+ * The Interrupt Collector Level Acknowledge Register is used by software to
+ * indicate the completion of an interrupt on a specific level.
+ * This register is written at the very end of an interrupt service routine. If
+ * nesting is used then the CPU irq must be turned on before writing to this
+ * register to avoid a race condition in the CPU interrupt hardware.
+ */
+#define ASM9260_HW_ICOLL_LEVELACK 0x0010
+#define ASM9260_BM_LEVELn(nr) BIT(nr)
+
+#define ASM9260_HW_ICOLL_CTRL 0x0020
+/*
+ * ASM9260_BM_CTRL_SFTRST and ASM9260_BM_CTRL_CLKGATE are not available on
+ * asm9260.
+ */
+#define ASM9260_BM_CTRL_SFTRST BIT(31)
+#define ASM9260_BM_CTRL_CLKGATE BIT(30)
+/* disable interrupt level nesting */
+#define ASM9260_BM_CTRL_NO_NESTING BIT(19)
+/*
+ * Set this bit to one enable the RISC32-style read side effect associated with
+ * the vector address register. In this mode, interrupt in-service is signaled
+ * by the read of the ASM9260_HW_ICOLL_VECTOR register to acquire the interrupt
+ * vector address. Set this bit to zero for normal operation, in which the ISR
+ * signals in-service explicitly by means of a write to the
+ * ASM9260_HW_ICOLL_VECTOR register.
+ * 0 - Must Write to Vector register to go in-service.
+ * 1 - Go in-service as a read side effect
+ */
+#define ASM9260_BM_CTRL_ARM_RSE_MODE BIT(18)
+#define ASM9260_BM_CTRL_IRQ_ENABLE BIT(16)
+
+#define ASM9260_HW_ICOLL_STAT_OFFSET 0x0030
+/*
+ * bits 5:0
+ * Vector number of current interrupt. Multiply by 4 and add to vector base
+ * address to obtain the value in ASM9260_HW_ICOLL_VECTOR.
+ */
+
+/*
+ * RAW0 and RAW1 provides a read-only view of the raw interrupt request lines
+ * coming from various parts of the chip. Its purpose is to improve diagnostic
+ * observability.
+ */
+#define ASM9260_HW_ICOLL_RAW0 0x0040
+#define ASM9260_HW_ICOLL_RAW1 0x0050
+
+#define ASM9260_HW_ICOLL_INTERRUPT0 0x0060
+#define ASM9260_HW_ICOLL_INTERRUPTn(n) (0x0060 + ((n) >> 2) * 0x10)
+/*
+ * WARNING: Modifying the priority of an enabled interrupt may result in
+ * undefined behavior.
+ */
+#define ASM9260_BM_INT_PRIORITY_MASK 0x3
+#define ASM9260_BM_INT_ENABLE BIT(2)
+#define ASM9260_BM_INT_SOFTIRQ BIT(3)
+
+#define ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n) (((n) & 0x3) << 3)
+#define ASM9260_BM_ICOLL_INTERRUPTn_ENABLE(n) (1 << (2 + \
+ ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n)))
+
+#define ASM9260_HW_ICOLL_VBASE 0x0160
+/*
+ * bits 31:2
+ * This bitfield holds the upper 30 bits of the base address of the vector
+ * table.
+ */
+
+#define ASM9260_HW_ICOLL_CLEAR0 0x01d0
+#define ASM9260_HW_ICOLL_CLEAR1 0x01e0
+#define ASM9260_HW_ICOLL_CLEARn(n) (((n >> 5) * 0x10) \
+ + SET_REG)
+#define ASM9260_BM_CLEAR_BIT(n) BIT(n & 0x1f)
+
+/* Scratchpad */
+#define ASM9260_HW_ICOLL_UNDEF_VECTOR 0x01f0
+#endif
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index cd7d3bc78..ead15be2d 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -144,7 +144,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
unsigned long *out_hwirq,
unsigned int *out_type)
{
- if (d->of_node != controller)
+ if (irq_domain_get_of_node(d) != controller)
return -EINVAL;
if (intsize < 2)
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 389318a3b..3f3a8c3d2 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -56,9 +56,6 @@
#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
-#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
-#define ARMADA_370_XP_FABRIC_IRQ (3)
-
#define IPI_DOORBELL_START (0)
#define IPI_DOORBELL_END (8)
#define IPI_DOORBELL_MASK 0xFF
@@ -81,13 +78,10 @@ static phys_addr_t msi_doorbell_addr;
static inline bool is_percpu_irq(irq_hw_number_t irq)
{
- switch (irq) {
- case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
- case ARMADA_370_XP_FABRIC_IRQ:
+ if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS)
return true;
- default:
- return false;
- }
+
+ return false;
}
/*
@@ -550,7 +544,7 @@ static void armada_370_xp_mpic_resume(void)
if (virq == 0)
continue;
- if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+ if (!is_percpu_irq(irq))
writel(irq, per_cpu_int_base +
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
else
diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
index 63cd031b2..b12a5d585 100644
--- a/drivers/irqchip/irq-atmel-aic-common.c
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -114,7 +114,7 @@ int aic_common_irq_domain_xlate(struct irq_domain *d,
static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
{
- struct device_node *node = domain->of_node;
+ struct device_node *node = irq_domain_get_of_node(domain);
struct irq_chip_generic *gc;
struct aic_chip_data *aic;
struct property *prop;
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index f6d680485..62bb840c6 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -70,16 +70,15 @@ 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];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
u32 irqnr;
u32 irqstat;
- irqnr = irq_reg_readl(gc, AT91_AIC5_IVR);
- irqstat = irq_reg_readl(gc, AT91_AIC5_ISR);
+ irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
+ irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
if (!irqstat)
- irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
+ irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
else
handle_domain_irq(aic5_domain, irqnr, regs);
}
@@ -87,8 +86,7 @@ aic5_handle(struct pt_regs *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 *bgc = dgc->gc[0];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
/*
@@ -105,8 +103,7 @@ static void aic5_mask(struct irq_data *d)
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 *bgc = dgc->gc[0];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
/*
@@ -123,14 +120,13 @@ static void aic5_unmask(struct irq_data *d)
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];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
/* Enable interrupt on AIC5 */
- irq_gc_lock(gc);
- irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
- irq_reg_writel(gc, 1, AT91_AIC5_ISCR);
- irq_gc_unlock(gc);
+ irq_gc_lock(bgc);
+ irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+ irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
+ irq_gc_unlock(bgc);
return 0;
}
@@ -138,18 +134,17 @@ static int aic5_retrigger(struct irq_data *d)
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];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
unsigned int smr;
int ret;
- irq_gc_lock(gc);
- irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
- smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+ irq_gc_lock(bgc);
+ irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+ smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
ret = aic_common_set_type(d, type, &smr);
if (!ret)
- irq_reg_writel(gc, smr, AT91_AIC5_SMR);
- irq_gc_unlock(gc);
+ irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
+ irq_gc_unlock(bgc);
return ret;
}
@@ -159,7 +154,7 @@ 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 *bgc = irq_get_domain_generic_chip(domain, 0);
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
int i;
u32 mask;
@@ -183,7 +178,7 @@ 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 *bgc = irq_get_domain_generic_chip(domain, 0);
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
int i;
u32 mask;
@@ -207,7 +202,7 @@ 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 *bgc = irq_get_domain_generic_chip(domain, 0);
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
int i;
@@ -262,12 +257,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
irq_hw_number_t *out_hwirq,
unsigned int *out_type)
{
- struct irq_domain_chip_generic *dgc = d->gc;
- struct irq_chip_generic *gc;
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
unsigned smr;
int ret;
- if (!dgc)
+ if (!bgc)
return -EINVAL;
ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
@@ -275,15 +269,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
if (ret)
return ret;
- gc = dgc->gc[0];
-
- irq_gc_lock(gc);
- irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR);
- smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+ irq_gc_lock(bgc);
+ irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
+ smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
ret = aic_common_set_priority(intspec[2], &smr);
if (!ret)
- irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR);
- irq_gc_unlock(gc);
+ irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR);
+ irq_gc_unlock(bgc);
return ret;
}
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index a7f562693..75573fa43 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -78,10 +78,13 @@ static struct irq_chip crossbar_chip = {
static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
irq_hw_number_t hwirq)
{
- struct of_phandle_args args;
+ struct irq_fwspec fwspec;
int i;
int err;
+ if (!irq_domain_get_of_node(domain->parent))
+ return -EINVAL;
+
raw_spin_lock(&cb->lock);
for (i = cb->int_max - 1; i >= 0; i--) {
if (cb->irq_map[i] == IRQ_FREE) {
@@ -94,13 +97,13 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
if (i < 0)
return -ENODEV;
- args.np = domain->parent->of_node;
- args.args_count = 3;
- args.args[0] = 0; /* SPI */
- args.args[1] = i;
- args.args[2] = IRQ_TYPE_LEVEL_HIGH;
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 3;
+ fwspec.param[0] = 0; /* SPI */
+ fwspec.param[1] = i;
+ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
- err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
if (err)
cb->irq_map[i] = IRQ_FREE;
else
@@ -112,16 +115,16 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *data)
{
- struct of_phandle_args *args = data;
+ struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
int i;
- if (args->args_count != 3)
+ if (fwspec->param_count != 3)
return -EINVAL; /* Not GIC compliant */
- if (args->args[0] != 0)
+ if (fwspec->param[0] != 0)
return -EINVAL; /* No PPI should point to this domain */
- hwirq = args->args[1];
+ hwirq = fwspec->param[1];
if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
return -EINVAL; /* Can't deal with this */
@@ -166,28 +169,31 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
raw_spin_unlock(&cb->lock);
}
-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)
+static int crossbar_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- if (d->of_node != controller)
- return -EINVAL; /* Shouldn't happen, really... */
- if (intsize != 3)
- return -EINVAL; /* Not GIC compliant */
- if (intspec[0] != 0)
- return -EINVAL; /* No PPI should point to this domain */
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
- *out_hwirq = intspec[1];
- *out_type = intspec[2];
- return 0;
+ /* No PPI should point to this domain */
+ if (fwspec->param[0] != 0)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2];
+ return 0;
+ }
+
+ return -EINVAL;
}
static const struct irq_domain_ops crossbar_domain_ops = {
- .alloc = crossbar_domain_alloc,
- .free = crossbar_domain_free,
- .xlate = crossbar_domain_xlate,
+ .alloc = crossbar_domain_alloc,
+ .free = crossbar_domain_free,
+ .translate = crossbar_domain_translate,
};
static int __init crossbar_of_init(struct device_node *node)
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index 9448e391c..f174ce0ca 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -21,6 +21,17 @@
#include "irq-gic-common.h"
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+ void *data)
+{
+ for (; quirks->desc; quirks++) {
+ if (quirks->iidr != (quirks->mask & iidr))
+ continue;
+ quirks->init(data);
+ pr_info("GIC: enabling workaround for %s\n", quirks->desc);
+ }
+}
+
int gic_configure_irq(unsigned int irq, unsigned int type,
void __iomem *base, void (*sync_access)(void))
{
@@ -73,12 +84,15 @@ void __init gic_dist_config(void __iomem *base, int gic_irqs,
writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
/*
- * Disable all interrupts. Leave the PPI and SGIs alone
- * as they are enabled by redistributor registers.
+ * Deactivate and disable all SPIs. Leave the PPI and SGIs
+ * alone as they are in the redistributor registers on GICv3.
*/
- for (i = 32; i < gic_irqs; i += 32)
+ for (i = 32; i < gic_irqs; i += 32) {
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ base + GIC_DIST_ACTIVE_CLEAR + i / 8);
writel_relaxed(GICD_INT_EN_CLR_X32,
- base + GIC_DIST_ENABLE_CLEAR + i / 8);
+ base + GIC_DIST_ENABLE_CLEAR + i / 8);
+ }
if (sync_access)
sync_access();
@@ -91,7 +105,9 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
/*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
+ * Make sure everything is deactivated.
*/
+ writel_relaxed(GICD_INT_EN_CLR_X32, base + GIC_DIST_ACTIVE_CLEAR);
writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
index 35a988477..fff697db8 100644
--- a/drivers/irqchip/irq-gic-common.h
+++ b/drivers/irqchip/irq-gic-common.h
@@ -20,10 +20,19 @@
#include <linux/of.h>
#include <linux/irqdomain.h>
+struct gic_quirk {
+ const char *desc;
+ void (*init)(void *data);
+ u32 iidr;
+ u32 mask;
+};
+
int 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));
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+ void *data);
#endif /* _IRQ_GIC_COMMON_H */
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index 12985daa6..87f8d104a 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -37,19 +37,31 @@
#define V2M_MSI_SETSPI_NS 0x040
#define V2M_MIN_SPI 32
#define V2M_MAX_SPI 1019
+#define V2M_MSI_IIDR 0xFCC
#define V2M_MSI_TYPER_BASE_SPI(x) \
(((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK)
+/* APM X-Gene with GICv2m MSI_IIDR register value */
+#define XGENE_GICV2M_MSI_IIDR 0x06000170
+
+/* List of flags for specific v2m implementation */
+#define GICV2M_NEEDS_SPI_OFFSET 0x00000001
+
+static LIST_HEAD(v2m_nodes);
+static DEFINE_SPINLOCK(v2m_lock);
+
struct v2m_data {
- spinlock_t msi_cnt_lock;
+ struct list_head entry;
+ struct device_node *node;
struct resource res; /* GICv2m resource */
void __iomem *base; /* GICv2m virt address */
u32 spi_start; /* The SPI number that MSIs start */
u32 nr_spis; /* The number of SPIs for MSIs */
unsigned long *bm; /* MSI vector bitmap */
+ u32 flags; /* v2m flags for specific implementation */
};
static void gicv2m_mask_msi_irq(struct irq_data *d)
@@ -98,6 +110,9 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
msg->address_hi = upper_32_bits(addr);
msg->address_lo = lower_32_bits(addr);
msg->data = data->hwirq;
+
+ if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
+ msg->data -= v2m->spi_start;
}
static struct irq_chip gicv2m_irq_chip = {
@@ -113,17 +128,21 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
unsigned int virq,
irq_hw_number_t hwirq)
{
- struct of_phandle_args args;
+ struct irq_fwspec fwspec;
struct irq_data *d;
int err;
- args.np = domain->parent->of_node;
- args.args_count = 3;
- args.args[0] = 0;
- args.args[1] = hwirq - 32;
- args.args[2] = IRQ_TYPE_EDGE_RISING;
+ if (is_of_node(domain->parent->fwnode)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 3;
+ fwspec.param[0] = 0;
+ fwspec.param[1] = hwirq - 32;
+ fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else {
+ return -EINVAL;
+ }
- err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
if (err)
return err;
@@ -143,27 +162,30 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq)
return;
}
- spin_lock(&v2m->msi_cnt_lock);
+ spin_lock(&v2m_lock);
__clear_bit(pos, v2m->bm);
- spin_unlock(&v2m->msi_cnt_lock);
+ spin_unlock(&v2m_lock);
}
static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
- struct v2m_data *v2m = domain->host_data;
+ struct v2m_data *v2m = NULL, *tmp;
int hwirq, offset, err = 0;
- spin_lock(&v2m->msi_cnt_lock);
- offset = find_first_zero_bit(v2m->bm, v2m->nr_spis);
- if (offset < v2m->nr_spis)
- __set_bit(offset, v2m->bm);
- else
- err = -ENOSPC;
- spin_unlock(&v2m->msi_cnt_lock);
+ spin_lock(&v2m_lock);
+ list_for_each_entry(tmp, &v2m_nodes, entry) {
+ offset = find_first_zero_bit(tmp->bm, tmp->nr_spis);
+ if (offset < tmp->nr_spis) {
+ __set_bit(offset, tmp->bm);
+ v2m = tmp;
+ break;
+ }
+ }
+ spin_unlock(&v2m_lock);
- if (err)
- return err;
+ if (!v2m)
+ return -ENOSPC;
hwirq = v2m->spi_start + offset;
@@ -224,12 +246,61 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = {
.chip = &gicv2m_pmsi_irq_chip,
};
+static void gicv2m_teardown(void)
+{
+ struct v2m_data *v2m, *tmp;
+
+ list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) {
+ list_del(&v2m->entry);
+ kfree(v2m->bm);
+ iounmap(v2m->base);
+ of_node_put(v2m->node);
+ kfree(v2m);
+ }
+}
+
+static int gicv2m_allocate_domains(struct irq_domain *parent)
+{
+ struct irq_domain *inner_domain, *pci_domain, *plat_domain;
+ struct v2m_data *v2m;
+
+ v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
+ if (!v2m)
+ return 0;
+
+ inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node),
+ &gicv2m_domain_ops, v2m);
+ if (!inner_domain) {
+ pr_err("Failed to create GICv2m domain\n");
+ return -ENOMEM;
+ }
+
+ inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+ inner_domain->parent = parent;
+ pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ &gicv2m_msi_domain_info,
+ inner_domain);
+ plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ &gicv2m_pmsi_domain_info,
+ inner_domain);
+ if (!pci_domain || !plat_domain) {
+ pr_err("Failed to create MSI domains\n");
+ if (plat_domain)
+ irq_domain_remove(plat_domain);
+ if (pci_domain)
+ irq_domain_remove(pci_domain);
+ irq_domain_remove(inner_domain);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int __init gicv2m_init_one(struct device_node *node,
struct irq_domain *parent)
{
int ret;
struct v2m_data *v2m;
- struct irq_domain *inner_domain, *pci_domain, *plat_domain;
v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
if (!v2m) {
@@ -237,6 +308,9 @@ static int __init gicv2m_init_one(struct device_node *node,
return -ENOMEM;
}
+ INIT_LIST_HEAD(&v2m->entry);
+ v2m->node = node;
+
ret = of_address_to_resource(node, 0, &v2m->res);
if (ret) {
pr_err("Failed to allocate v2m resource.\n");
@@ -266,6 +340,17 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_iounmap;
}
+ /*
+ * APM X-Gene GICv2m implementation has an erratum where
+ * the MSI data needs to be the offset from the spi_start
+ * in order to trigger the correct MSI interrupt. This is
+ * different from the standard GICv2m implementation where
+ * the MSI data is the absolute value within the range from
+ * spi_start to (spi_start + num_spis).
+ */
+ if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR)
+ v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+
v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis),
GFP_KERNEL);
if (!v2m->bm) {
@@ -273,43 +358,13 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_iounmap;
}
- inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m);
- if (!inner_domain) {
- pr_err("Failed to create GICv2m domain\n");
- ret = -ENOMEM;
- goto err_free_bm;
- }
-
- inner_domain->bus_token = DOMAIN_BUS_NEXUS;
- inner_domain->parent = parent;
- pci_domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info,
- inner_domain);
- plat_domain = platform_msi_create_irq_domain(node,
- &gicv2m_pmsi_domain_info,
- inner_domain);
- if (!pci_domain || !plat_domain) {
- pr_err("Failed to create MSI domains\n");
- ret = -ENOMEM;
- goto err_free_domains;
- }
-
- spin_lock_init(&v2m->msi_cnt_lock);
-
+ list_add_tail(&v2m->entry, &v2m_nodes);
pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
(unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
return 0;
-err_free_domains:
- if (plat_domain)
- irq_domain_remove(plat_domain);
- if (pci_domain)
- irq_domain_remove(pci_domain);
- if (inner_domain)
- irq_domain_remove(inner_domain);
-err_free_bm:
- kfree(v2m->bm);
err_iounmap:
iounmap(v2m->base);
err_free_v2m:
@@ -339,5 +394,9 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
}
}
+ if (!ret)
+ ret = gicv2m_allocate_domains(parent);
+ if (ret)
+ gicv2m_teardown();
return ret;
}
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
index a7c8c9ffb..aee60ed02 100644
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -42,7 +42,6 @@ static struct irq_chip its_msi_irq_chip = {
struct its_pci_alias {
struct pci_dev *pdev;
- u32 dev_id;
u32 count;
};
@@ -60,7 +59,6 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
{
struct its_pci_alias *dev_alias = data;
- dev_alias->dev_id = alias;
if (pdev != dev_alias->pdev)
dev_alias->count += its_pci_msi_vec_count(pdev);
@@ -86,7 +84,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
/* ITS specific DeviceID, as the core ITS ignores dev. */
- info->scratchpad[0].ul = dev_alias.dev_id;
+ info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
return msi_info->ops->msi_prepare(domain->parent,
dev, dev_alias.count, info);
@@ -125,7 +123,8 @@ static int __init its_pci_msi_init(void)
continue;
}
- if (!pci_msi_create_irq_domain(np, &its_pci_msi_domain_info,
+ if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
+ &its_pci_msi_domain_info,
parent)) {
pr_err("%s: unable to create PCI domain\n",
np->full_name);
diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c
index a86550562..470b4aa7d 100644
--- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c
@@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
{
struct msi_domain_info *msi_info;
u32 dev_id;
- int ret;
+ int ret, index = 0;
msi_info = msi_get_domain_info(domain->parent);
/* Suck the DeviceID out of the msi-parent property */
- ret = of_property_read_u32_index(dev->of_node, "msi-parent",
- 1, &dev_id);
+ do {
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(dev->of_node,
+ "msi-parent", "#msi-cells",
+ index, &args);
+ if (args.np == irq_domain_get_of_node(domain)) {
+ if (WARN_ON(args.args_count != 1))
+ return -EINVAL;
+ dev_id = args.args[0];
+ break;
+ }
+ } while (!ret);
+
if (ret)
return ret;
@@ -78,7 +90,8 @@ static int __init its_pmsi_init(void)
continue;
}
- if (!platform_msi_create_irq_domain(np, &its_pmsi_domain_info,
+ if (!platform_msi_create_irq_domain(of_node_to_fwnode(np),
+ &its_pmsi_domain_info,
parent)) {
pr_err("%s: unable to create platform domain\n",
np->full_name);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 25ceae9f7..e23d1d18f 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -37,7 +37,10 @@
#include <asm/cputype.h>
#include <asm/exception.h>
-#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0)
+#include "irq-gic-common.h"
+
+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
+#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
@@ -817,7 +820,22 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
int i;
int psz = SZ_64K;
u64 shr = GITS_BASER_InnerShareable;
- u64 cache = GITS_BASER_WaWb;
+ u64 cache;
+ u64 typer;
+ u32 ids;
+
+ if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
+ /*
+ * erratum 22375: only alloc 8MB table size
+ * erratum 24313: ignore memory access type
+ */
+ cache = 0;
+ ids = 0x14; /* 20 bits, 8MB */
+ } else {
+ cache = GITS_BASER_WaWb;
+ typer = readq_relaxed(its->base + GITS_TYPER);
+ ids = GITS_TYPER_DEVBITS(typer);
+ }
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -825,6 +843,7 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
int order = get_order(psz);
int alloc_size;
+ int alloc_pages;
u64 tmp;
void *base;
@@ -840,9 +859,6 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
* For other tables, only allocate a single page.
*/
if (type == GITS_BASER_TYPE_DEVICE) {
- u64 typer = readq_relaxed(its->base + GITS_TYPER);
- u32 ids = GITS_TYPER_DEVBITS(typer);
-
/*
* 'order' was initialized earlier to the default page
* granule of the the ITS. We can't have an allocation
@@ -859,6 +875,14 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
}
alloc_size = (1 << order) * PAGE_SIZE;
+ alloc_pages = (alloc_size / psz);
+ if (alloc_pages > GITS_BASER_PAGES_MAX) {
+ alloc_pages = GITS_BASER_PAGES_MAX;
+ order = get_order(GITS_BASER_PAGES_MAX * psz);
+ pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
+ node_name, order, alloc_pages);
+ }
+
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!base) {
err = -ENOMEM;
@@ -887,7 +911,7 @@ retry_baser:
break;
}
- val |= (alloc_size / psz) - 1;
+ val |= alloc_pages - 1;
writeq_relaxed(val, its->base + GITS_BASER + i * 8);
tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -1241,15 +1265,19 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
unsigned int virq,
irq_hw_number_t hwirq)
{
- struct of_phandle_args args;
+ struct irq_fwspec fwspec;
- args.np = domain->parent->of_node;
- args.args_count = 3;
- args.args[0] = GIC_IRQ_TYPE_LPI;
- args.args[1] = hwirq;
- args.args[2] = IRQ_TYPE_EDGE_RISING;
+ if (irq_domain_get_of_node(domain->parent)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 3;
+ fwspec.param[0] = GIC_IRQ_TYPE_LPI;
+ fwspec.param[1] = hwirq;
+ fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else {
+ return -EINVAL;
+ }
- return irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+ return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
}
static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -1370,6 +1398,33 @@ static int its_force_quiescent(void __iomem *base)
}
}
+static void __maybe_unused its_enable_quirk_cavium_22375(void *data)
+{
+ struct its_node *its = data;
+
+ its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
+}
+
+static const struct gic_quirk its_quirks[] = {
+#ifdef CONFIG_CAVIUM_ERRATUM_22375
+ {
+ .desc = "ITS: Cavium errata 22375, 24313",
+ .iidr = 0xa100034c, /* ThunderX pass 1.x */
+ .mask = 0xffff0fff,
+ .init = its_enable_quirk_cavium_22375,
+ },
+#endif
+ {
+ }
+};
+
+static void its_enable_quirks(struct its_node *its)
+{
+ u32 iidr = readl_relaxed(its->base + GITS_IIDR);
+
+ gic_enable_quirks(iidr, its_quirks, its);
+}
+
static int its_probe(struct device_node *node, struct irq_domain *parent)
{
struct resource res;
@@ -1428,6 +1483,8 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
}
its->cmd_write = its->cmd_base;
+ its_enable_quirks(its);
+
err = its_alloc_tables(node->full_name, its);
if (err)
goto out_free_cmd;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 36ecfc870..d7be6ddc3 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -108,57 +108,17 @@ static void gic_redist_wait_for_rwp(void)
gic_do_wait_for_rwp(gic_data_rdist_rd_base());
}
-/* Low level accessors */
-static u64 __maybe_unused gic_read_iar(void)
-{
- u64 irqstat;
-
- asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
- return irqstat;
-}
-
-static void __maybe_unused gic_write_pmr(u64 val)
-{
- asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val));
-}
-
-static void __maybe_unused gic_write_ctlr(u64 val)
-{
- asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val));
- isb();
-}
-
-static void __maybe_unused gic_write_grpen1(u64 val)
-{
- asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val));
- isb();
-}
+#ifdef CONFIG_ARM64
+static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
-static void __maybe_unused gic_write_sgi1r(u64 val)
-{
- asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
-}
-
-static void gic_enable_sre(void)
+static u64 __maybe_unused gic_read_iar(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");
+ if (static_branch_unlikely(&is_cavium_thunderx))
+ return gic_read_iar_cavium_thunderx();
+ else
+ return gic_read_iar_common();
}
+#endif
static void gic_enable_redist(bool enable)
{
@@ -359,11 +319,11 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
return 0;
}
-static u64 gic_mpidr_to_affinity(u64 mpidr)
+static u64 gic_mpidr_to_affinity(unsigned long mpidr)
{
u64 aff;
- aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+ aff = ((u64)MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
MPIDR_AFFINITY_LEVEL(mpidr, 0));
@@ -373,7 +333,7 @@ static u64 gic_mpidr_to_affinity(u64 mpidr)
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
- u64 irqnr;
+ u32 irqnr;
do {
irqnr = gic_read_iar();
@@ -432,12 +392,12 @@ static void __init gic_dist_init(void)
*/
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);
+ gic_write_irouter(affinity, base + GICD_IROUTER + i * 8);
}
static int gic_populate_rdist(void)
{
- u64 mpidr = cpu_logical_map(smp_processor_id());
+ unsigned long mpidr = cpu_logical_map(smp_processor_id());
u64 typer;
u32 aff;
int i;
@@ -463,15 +423,14 @@ static int gic_populate_rdist(void)
}
do {
- typer = readq_relaxed(ptr + GICR_TYPER);
+ typer = gic_read_typer(ptr + GICR_TYPER);
if ((typer >> 32) == aff) {
u64 offset = ptr - gic_data.redist_regions[i].redist_base;
gic_data_rdist_rd_base() = ptr;
gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
- pr_info("CPU%d: found redistributor %llx region %d:%pa\n",
- smp_processor_id(),
- (unsigned long long)mpidr,
- i, &gic_data_rdist()->phys_base);
+ pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
+ smp_processor_id(), mpidr, i,
+ &gic_data_rdist()->phys_base);
return 0;
}
@@ -486,15 +445,22 @@ static int gic_populate_rdist(void)
}
/* 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);
+ WARN(true, "CPU%d: mpidr %lx has no re-distributor!\n",
+ smp_processor_id(), mpidr);
return -ENODEV;
}
static void gic_cpu_sys_reg_init(void)
{
- /* Enable system registers */
- gic_enable_sre();
+ /*
+ * 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.
+ */
+ if (!gic_enable_sre())
+ pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
/* Set priority mask register */
gic_write_pmr(DEFAULT_PMR_VALUE);
@@ -557,10 +523,10 @@ static struct notifier_block gic_cpu_notifier = {
};
static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
- u64 cluster_id)
+ unsigned long cluster_id)
{
int cpu = *base_cpu;
- u64 mpidr = cpu_logical_map(cpu);
+ unsigned long mpidr = cpu_logical_map(cpu);
u16 tlist = 0;
while (cpu < nr_cpu_ids) {
@@ -621,7 +587,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
smp_wmb();
for_each_cpu(cpu, mask) {
- u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL;
+ unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
u16 tlist;
tlist = gic_compute_target_list(&cpu, mask, cluster_id);
@@ -657,7 +623,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);
val = gic_mpidr_to_affinity(cpu_logical_map(cpu));
- writeq_relaxed(val, reg);
+ gic_write_irouter(val, reg);
/*
* If the interrupt was enabled, enabled it again. Otherwise,
@@ -771,32 +737,34 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
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)
+static int gic_irq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- if (d->of_node != controller)
- return -EINVAL;
- if (intsize < 3)
- return -EINVAL;
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count < 3)
+ return -EINVAL;
- switch(intspec[0]) {
- case 0: /* SPI */
- *out_hwirq = intspec[1] + 32;
- break;
- case 1: /* PPI */
- *out_hwirq = intspec[1] + 16;
- break;
- case GIC_IRQ_TYPE_LPI: /* LPI */
- *out_hwirq = intspec[1];
- break;
- default:
- return -EINVAL;
+ switch (fwspec->param[0]) {
+ case 0: /* SPI */
+ *hwirq = fwspec->param[1] + 32;
+ break;
+ case 1: /* PPI */
+ *hwirq = fwspec->param[1] + 16;
+ break;
+ case GIC_IRQ_TYPE_LPI: /* LPI */
+ *hwirq = fwspec->param[1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
}
- *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
- return 0;
+ return -EINVAL;
}
static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -805,10 +773,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
int i, ret;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
- struct of_phandle_args *irq_data = arg;
+ struct irq_fwspec *fwspec = arg;
- ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
- irq_data->args_count, &hwirq, &type);
+ ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
@@ -831,11 +798,19 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
}
static const struct irq_domain_ops gic_irq_domain_ops = {
- .xlate = gic_irq_domain_xlate,
+ .translate = gic_irq_domain_translate,
.alloc = gic_irq_domain_alloc,
.free = gic_irq_domain_free,
};
+static void gicv3_enable_quirks(void)
+{
+#ifdef CONFIG_ARM64
+ if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_23154))
+ static_branch_enable(&is_cavium_thunderx);
+#endif
+}
+
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
@@ -901,6 +876,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
gic_data.nr_redist_regions = nr_redist_regions;
gic_data.redist_stride = redist_stride;
+ gicv3_enable_quirks();
+
/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 982c09c2d..abf2ffaed 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -41,7 +41,6 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
-#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -51,6 +50,19 @@
#include "irq-gic-common.h"
+#ifdef CONFIG_ARM64
+#include <asm/cpufeature.h>
+
+static void gic_check_cpu_features(void)
+{
+ WARN_TAINT_ONCE(cpus_have_cap(ARM64_HAS_SYSREG_GIC_CPUIF),
+ TAINT_CPU_OUT_OF_SPEC,
+ "GICv3 system registers enabled, broken firmware!\n");
+}
+#else
+#define gic_check_cpu_features() do { } while(0)
+#endif
+
union gic_base {
void __iomem *common_base;
void __percpu * __iomem *percpu_base;
@@ -61,9 +73,11 @@ struct gic_chip_data {
union gic_base cpu_base;
#ifdef CONFIG_CPU_PM
u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+ u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
u32 __percpu *saved_ppi_enable;
+ u32 __percpu *saved_ppi_active;
u32 __percpu *saved_ppi_conf;
#endif
struct irq_domain *domain;
@@ -554,6 +568,10 @@ static void gic_dist_save(unsigned int gic_nr)
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
gic_data[gic_nr].saved_spi_enable[i] =
readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ gic_data[gic_nr].saved_spi_active[i] =
+ readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4);
}
/*
@@ -592,9 +610,19 @@ static void gic_dist_restore(unsigned int gic_nr)
writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
dist_base + GIC_DIST_TARGET + i * 4);
- for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) {
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ dist_base + GIC_DIST_ENABLE_CLEAR + i * 4);
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) {
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4);
+ writel_relaxed(gic_data[gic_nr].saved_spi_active[i],
+ dist_base + GIC_DIST_ACTIVE_SET + i * 4);
+ }
writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
}
@@ -619,6 +647,10 @@ static void gic_cpu_save(unsigned int gic_nr)
for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active);
+ for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+ ptr[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4);
+
ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
@@ -642,8 +674,18 @@ static void gic_cpu_restore(unsigned int gic_nr)
return;
ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
- for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+ for (i = 0; i < DIV_ROUND_UP(32, 32); i++) {
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ dist_base + GIC_DIST_ENABLE_CLEAR + i * 4);
writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ }
+
+ ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active);
+ for (i = 0; i < DIV_ROUND_UP(32, 32); i++) {
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4);
+ writel_relaxed(ptr[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4);
+ }
ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
@@ -698,6 +740,10 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
sizeof(u32));
BUG_ON(!gic->saved_ppi_enable);
+ gic->saved_ppi_active = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
+ sizeof(u32));
+ BUG_ON(!gic->saved_ppi_active);
+
gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
sizeof(u32));
BUG_ON(!gic->saved_ppi_conf);
@@ -903,28 +949,39 @@ static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int 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)
+static int gic_irq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- unsigned long ret = 0;
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count < 3)
+ return -EINVAL;
- if (d->of_node != controller)
- return -EINVAL;
- if (intsize < 3)
- return -EINVAL;
+ /* Get the interrupt number and add 16 to skip over SGIs */
+ *hwirq = fwspec->param[1] + 16;
- /* Get the interrupt number and add 16 to skip over SGIs */
- *out_hwirq = intspec[1] + 16;
+ /*
+ * For SPIs, we need to add 16 more to get the GIC irq
+ * ID number
+ */
+ if (!fwspec->param[0])
+ *hwirq += 16;
- /* For SPIs, we need to add 16 more to get the GIC irq ID number */
- if (!intspec[0])
- *out_hwirq += 16;
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
- *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+ if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+ if(fwspec->param_count != 2)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1];
+ return 0;
+ }
- return ret;
+ return -EINVAL;
}
#ifdef CONFIG_SMP
@@ -952,10 +1009,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
int i, ret;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
- struct of_phandle_args *irq_data = arg;
+ struct irq_fwspec *fwspec = arg;
- ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
- irq_data->args_count, &hwirq, &type);
+ ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
@@ -966,7 +1022,7 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
}
static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
- .xlate = gic_irq_domain_xlate,
+ .translate = gic_irq_domain_translate,
.alloc = gic_irq_domain_alloc,
.free = irq_domain_free_irqs_top,
};
@@ -974,12 +1030,11 @@ static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
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,
};
static 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)
+ u32 percpu_offset, struct fwnode_handle *handle)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
@@ -987,6 +1042,8 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
BUG_ON(gic_nr >= MAX_GIC_NR);
+ gic_check_cpu_features();
+
gic = &gic_data[gic_nr];
#ifdef CONFIG_GIC_NON_BANKED
if (percpu_offset) { /* Frankein-GIC without banked registers... */
@@ -1031,11 +1088,11 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
- if (node) { /* DT case */
- gic->domain = irq_domain_add_linear(node, gic_irqs,
- &gic_irq_domain_hierarchy_ops,
- gic);
- } else { /* Non-DT case */
+ if (handle) { /* DT/ACPI */
+ gic->domain = irq_domain_create_linear(handle, gic_irqs,
+ &gic_irq_domain_hierarchy_ops,
+ gic);
+ } else { /* Legacy support */
/*
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
@@ -1058,7 +1115,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
irq_base = irq_start;
}
- gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+ gic->domain = irq_domain_add_legacy(NULL, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
}
@@ -1087,17 +1144,15 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
gic_pm_init(gic);
}
-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)
+void __init gic_init(unsigned int gic_nr, int irq_start,
+ void __iomem *dist_base, void __iomem *cpu_base)
{
/*
* Non-DT/ACPI systems won't run a hypervisor, so let's not
* bother with these...
*/
static_key_slow_dec(&supports_deactivate);
- __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base,
- percpu_offset, node);
+ __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL);
}
#ifdef CONFIG_OF
@@ -1168,7 +1223,8 @@ gic_of_init(struct device_node *node, struct device_node *parent)
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
percpu_offset = 0;
- __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
+ __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,
+ &node->fwnode);
if (!gic_cnt)
gic_init_physaddr(node);
@@ -1191,11 +1247,12 @@ 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);
+IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init);
#endif
#ifdef CONFIG_ACPI
-static phys_addr_t dist_phy_base, cpu_phy_base __initdata;
+static phys_addr_t cpu_phy_base __initdata;
static int __init
gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
@@ -1223,60 +1280,57 @@ gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
return 0;
}
-static int __init
-gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header,
- const unsigned long end)
+/* The things you have to do to just *count* something... */
+static int __init acpi_dummy_func(struct acpi_subtable_header *header,
+ const unsigned long end)
{
- struct acpi_madt_generic_distributor *dist;
+ return 0;
+}
- dist = (struct acpi_madt_generic_distributor *)header;
+static bool __init acpi_gic_redist_is_present(void)
+{
+ return acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+ acpi_dummy_func, 0) > 0;
+}
- if (BAD_MADT_ENTRY(dist, end))
- return -EINVAL;
+static bool __init gic_validate_dist(struct acpi_subtable_header *header,
+ struct acpi_probe_entry *ape)
+{
+ struct acpi_madt_generic_distributor *dist;
+ dist = (struct acpi_madt_generic_distributor *)header;
- dist_phy_base = dist->base_address;
- return 0;
+ return (dist->version == ape->driver_data &&
+ (dist->version != ACPI_MADT_GIC_VERSION_NONE ||
+ !acpi_gic_redist_is_present()));
}
-int __init
-gic_v2_acpi_init(struct acpi_table_header *table)
+#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K)
+#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K)
+
+static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
+ const unsigned long end)
{
+ struct acpi_madt_generic_distributor *dist;
void __iomem *cpu_base, *dist_base;
+ struct fwnode_handle *domain_handle;
int count;
/* Collect CPU base addresses */
- count = acpi_parse_entries(ACPI_SIG_MADT,
- sizeof(struct acpi_table_madt),
- gic_acpi_parse_madt_cpu, table,
- ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ gic_acpi_parse_madt_cpu, 0);
if (count <= 0) {
pr_err("No valid GICC entries exist\n");
return -EINVAL;
}
- /*
- * Find distributor base address. We expect one distributor entry since
- * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade.
- */
- count = acpi_parse_entries(ACPI_SIG_MADT,
- sizeof(struct acpi_table_madt),
- gic_acpi_parse_madt_distributor, table,
- ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0);
- if (count <= 0) {
- pr_err("No valid GICD entries exist\n");
- return -EINVAL;
- } else if (count > 1) {
- pr_err("More than one GICD entry detected\n");
- return -EINVAL;
- }
-
cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
if (!cpu_base) {
pr_err("Unable to map GICC registers\n");
return -ENOMEM;
}
- dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE);
+ dist = (struct acpi_madt_generic_distributor *)header;
+ dist_base = ioremap(dist->base_address, ACPI_GICV2_DIST_MEM_SIZE);
if (!dist_base) {
pr_err("Unable to map GICD registers\n");
iounmap(cpu_base);
@@ -1292,14 +1346,25 @@ gic_v2_acpi_init(struct acpi_table_header *table)
static_key_slow_dec(&supports_deactivate);
/*
- * Initialize zero GIC instance (no multi-GIC support). Also, set GIC
- * as default IRQ domain to allow for GSI registration and GSI to IRQ
- * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
+ * Initialize GIC instance zero (no multi-GIC support).
*/
- __gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
- irq_set_default_host(gic_data[0].domain);
+ domain_handle = irq_domain_alloc_fwnode(dist_base);
+ if (!domain_handle) {
+ pr_err("Unable to allocate domain handle\n");
+ iounmap(cpu_base);
+ iounmap(dist_base);
+ return -ENOMEM;
+ }
+
+ __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
- acpi_irq_model = ACPI_IRQ_MODEL_GIC;
+ acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
return 0;
}
+IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ gic_validate_dist, ACPI_MADT_GIC_VERSION_V2,
+ gic_v2_acpi_init);
+IRQCHIP_ACPI_DECLARE(gic_v2_maybe, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ gic_validate_dist, ACPI_MADT_GIC_VERSION_NONE,
+ gic_v2_acpi_init);
#endif
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
index 8f3ca8f3a..9688d2e2a 100644
--- a/drivers/irqchip/irq-hip04.c
+++ b/drivers/irqchip/irq-hip04.c
@@ -325,7 +325,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
{
unsigned long ret = 0;
- if (d->of_node != controller)
+ if (irq_domain_get_of_node(d) != controller)
return -EINVAL;
if (intsize < 3)
return -EINVAL;
diff --git a/drivers/irqchip/irq-i8259.c b/drivers/irqchip/irq-i8259.c
index e484fd255..6b304eb39 100644
--- a/drivers/irqchip/irq-i8259.c
+++ b/drivers/irqchip/irq-i8259.c
@@ -377,8 +377,8 @@ int __init i8259_of_init(struct device_node *node, struct device_node *parent)
}
domain = __init_i8259_irqs(node);
- irq_set_handler_data(parent_irq, domain);
- irq_set_chained_handler(parent_irq, i8259_irq_dispatch);
+ irq_set_chained_handler_and_data(parent_irq, i8259_irq_dispatch,
+ domain);
return 0;
}
IRQCHIP_DECLARE(i8259, "intel,i8259", i8259_of_init);
diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c
index e48d33054..15af9a975 100644
--- a/drivers/irqchip/irq-imx-gpcv2.c
+++ b/drivers/irqchip/irq-imx-gpcv2.c
@@ -150,49 +150,42 @@ static struct irq_chip gpcv2_irqchip_data_chip = {
#endif
};
-static int imx_gpcv2_domain_xlate(struct irq_domain *domain,
- struct device_node *controller,
- const u32 *intspec,
- unsigned int intsize,
- unsigned long *out_hwirq,
- unsigned int *out_type)
+static int imx_gpcv2_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- /* Shouldn't happen, really... */
- if (domain->of_node != controller)
- return -EINVAL;
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
- /* Not GIC compliant */
- if (intsize != 3)
- return -EINVAL;
+ /* No PPI should point to this domain */
+ if (fwspec->param[0] != 0)
+ return -EINVAL;
- /* No PPI should point to this domain */
- if (intspec[0] != 0)
- return -EINVAL;
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2];
+ return 0;
+ }
- *out_hwirq = intspec[1];
- *out_type = intspec[2];
- return 0;
+ return -EINVAL;
}
static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
unsigned int irq, unsigned int nr_irqs,
void *data)
{
- struct of_phandle_args *args = data;
- struct of_phandle_args parent_args;
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
irq_hw_number_t hwirq;
+ unsigned int type;
+ int err;
int i;
- /* Not GIC compliant */
- if (args->args_count != 3)
- return -EINVAL;
-
- /* No PPI should point to this domain */
- if (args->args[0] != 0)
- return -EINVAL;
+ err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
+ if (err)
+ return err;
- /* Can't deal with this */
- hwirq = args->args[1];
if (hwirq >= GPC_MAX_IRQS)
return -EINVAL;
@@ -201,15 +194,16 @@ static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
&gpcv2_irqchip_data_chip, domain->host_data);
}
- parent_args = *args;
- parent_args.np = domain->parent->of_node;
- return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+ parent_fwspec = *fwspec;
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+ &parent_fwspec);
}
static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
- .xlate = imx_gpcv2_domain_xlate,
- .alloc = imx_gpcv2_domain_alloc,
- .free = irq_domain_free_irqs_common,
+ .translate = imx_gpcv2_domain_translate,
+ .alloc = imx_gpcv2_domain_alloc,
+ .free = irq_domain_free_irqs_common,
};
static int __init imx_gpcv2_irqchip_init(struct device_node *node,
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index aeaa061f0..9e17ef27a 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -29,6 +29,7 @@ struct gic_pcpu_mask {
DECLARE_BITMAP(pcpu_mask, GIC_MAX_INTRS);
};
+static unsigned long __gic_base_addr;
static void __iomem *gic_base;
static struct gic_pcpu_mask pcpu_masks[NR_CPUS];
static DEFINE_SPINLOCK(gic_lock);
@@ -301,6 +302,17 @@ int gic_get_c0_fdc_int(void)
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
}
+int gic_get_usm_range(struct resource *gic_usm_res)
+{
+ if (!gic_present)
+ return -1;
+
+ gic_usm_res->start = __gic_base_addr + USM_VISIBLE_SECTION_OFS;
+ gic_usm_res->end = gic_usm_res->start + (USM_VISIBLE_SECTION_SIZE - 1);
+
+ return 0;
+}
+
static void gic_handle_shared_int(bool chained)
{
unsigned int i, intr, virq, gic_reg_step = mips_cm_is64 ? 8 : 4;
@@ -798,6 +810,8 @@ static void __init __gic_init(unsigned long gic_base_addr,
{
unsigned int gicconfig;
+ __gic_base_addr = gic_base_addr;
+
gic_base = ioremap_nocache(gic_base_addr, gic_addrspace_size);
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c
index c8753da4c..63ac73b1d 100644
--- a/drivers/irqchip/irq-mtk-sysirq.c
+++ b/drivers/irqchip/irq-mtk-sysirq.c
@@ -67,22 +67,25 @@ static struct irq_chip mtk_sysirq_chip = {
.irq_set_affinity = irq_chip_set_affinity_parent,
};
-static int mtk_sysirq_domain_xlate(struct irq_domain *d,
- struct device_node *controller,
- const u32 *intspec, unsigned int intsize,
- unsigned long *out_hwirq,
- unsigned int *out_type)
+static int mtk_sysirq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- if (intsize != 3)
- return -EINVAL;
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
- /* sysirq doesn't support PPI */
- if (intspec[0])
- return -EINVAL;
+ /* No PPI should point to this domain */
+ if (fwspec->param[0] != 0)
+ return -EINVAL;
- *out_hwirq = intspec[1];
- *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
- return 0;
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
+
+ return -EINVAL;
}
static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -90,30 +93,30 @@ static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
{
int i;
irq_hw_number_t hwirq;
- struct of_phandle_args *irq_data = arg;
- struct of_phandle_args gic_data = *irq_data;
+ struct irq_fwspec *fwspec = arg;
+ struct irq_fwspec gic_fwspec = *fwspec;
- if (irq_data->args_count != 3)
+ if (fwspec->param_count != 3)
return -EINVAL;
/* sysirq doesn't support PPI */
- if (irq_data->args[0])
+ if (fwspec->param[0])
return -EINVAL;
- hwirq = irq_data->args[1];
+ hwirq = fwspec->param[1];
for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&mtk_sysirq_chip,
domain->host_data);
- gic_data.np = domain->parent->of_node;
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+ gic_fwspec.fwnode = domain->parent->fwnode;
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
}
static const struct irq_domain_ops sysirq_domain_ops = {
- .xlate = mtk_sysirq_domain_xlate,
- .alloc = mtk_sysirq_domain_alloc,
- .free = irq_domain_free_irqs_common,
+ .translate = mtk_sysirq_domain_translate,
+ .alloc = mtk_sysirq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
};
static int __init mtk_sysirq_of_init(struct device_node *node,
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index 604df63e2..c22e2d40c 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ * Add Alphascale ASM9260 support.
*
* 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
@@ -28,20 +30,64 @@
#include <linux/stmp_device.h>
#include <asm/exception.h>
+#include "alphascale_asm9260-icoll.h"
+
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+#define SET_REG 4
+#define CLR_REG 8
+
#define HW_ICOLL_VECTOR 0x0000
#define HW_ICOLL_LEVELACK 0x0010
#define HW_ICOLL_CTRL 0x0020
#define HW_ICOLL_STAT_OFFSET 0x0070
-#define HW_ICOLL_INTERRUPTn_SET(n) (0x0124 + (n) * 0x10)
-#define HW_ICOLL_INTERRUPTn_CLR(n) (0x0128 + (n) * 0x10)
-#define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004
+#define HW_ICOLL_INTERRUPT0 0x0120
+#define HW_ICOLL_INTERRUPTn(n) ((n) * 0x10)
+#define BM_ICOLL_INTR_ENABLE BIT(2)
#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1
#define ICOLL_NUM_IRQS 128
-static void __iomem *icoll_base;
+enum icoll_type {
+ ICOLL,
+ ASM9260_ICOLL,
+};
+
+struct icoll_priv {
+ void __iomem *vector;
+ void __iomem *levelack;
+ void __iomem *ctrl;
+ void __iomem *stat;
+ void __iomem *intr;
+ void __iomem *clear;
+ enum icoll_type type;
+};
+
+static struct icoll_priv icoll_priv;
static struct irq_domain *icoll_domain;
+/* calculate bit offset depending on number of intterupt per register */
+static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
+{
+ /*
+ * mask lower part of hwirq to convert it
+ * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
+ */
+ return bit << ((d->hwirq & 3) << 3);
+}
+
+/* calculate mem offset depending on number of intterupt per register */
+static void __iomem *icoll_intr_reg(struct irq_data *d)
+{
+ /* offset = hwirq / intr_per_reg * 0x10 */
+ return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
+}
+
static void icoll_ack_irq(struct irq_data *d)
{
/*
@@ -50,19 +96,35 @@ static void icoll_ack_irq(struct irq_data *d)
* BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
*/
__raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
- icoll_base + HW_ICOLL_LEVELACK);
+ icoll_priv.levelack);
}
static void icoll_mask_irq(struct irq_data *d)
{
- __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
- icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq));
+ __raw_writel(BM_ICOLL_INTR_ENABLE,
+ icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
}
static void icoll_unmask_irq(struct irq_data *d)
{
- __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
- icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq));
+ __raw_writel(BM_ICOLL_INTR_ENABLE,
+ icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
+}
+
+static void asm9260_mask_irq(struct irq_data *d)
+{
+ __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+ icoll_intr_reg(d) + CLR_REG);
+}
+
+static void asm9260_unmask_irq(struct irq_data *d)
+{
+ __raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
+ icoll_priv.clear +
+ ASM9260_HW_ICOLL_CLEARn(d->hwirq));
+
+ __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+ icoll_intr_reg(d) + SET_REG);
}
static struct irq_chip mxs_icoll_chip = {
@@ -71,19 +133,32 @@ static struct irq_chip mxs_icoll_chip = {
.irq_unmask = icoll_unmask_irq,
};
+static struct irq_chip asm9260_icoll_chip = {
+ .irq_ack = icoll_ack_irq,
+ .irq_mask = asm9260_mask_irq,
+ .irq_unmask = asm9260_unmask_irq,
+};
+
asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
{
u32 irqnr;
- irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
- __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
+ irqnr = __raw_readl(icoll_priv.stat);
+ __raw_writel(irqnr, icoll_priv.vector);
handle_domain_irq(icoll_domain, irqnr, regs);
}
static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
- irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
+ struct irq_chip *chip;
+
+ if (icoll_priv.type == ICOLL)
+ chip = &mxs_icoll_chip;
+ else
+ chip = &asm9260_icoll_chip;
+
+ irq_set_chip_and_handler(virq, chip, handle_level_irq);
return 0;
}
@@ -93,20 +168,80 @@ static const struct irq_domain_ops icoll_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
};
+static void __init icoll_add_domain(struct device_node *np,
+ int num)
+{
+ icoll_domain = irq_domain_add_linear(np, num,
+ &icoll_irq_domain_ops, NULL);
+
+ if (!icoll_domain)
+ panic("%s: unable to create irq domain", np->full_name);
+}
+
+static void __iomem * __init icoll_init_iobase(struct device_node *np)
+{
+ void __iomem *icoll_base;
+
+ icoll_base = of_io_request_and_map(np, 0, np->name);
+ if (!icoll_base)
+ panic("%s: unable to map resource", np->full_name);
+ return icoll_base;
+}
+
static int __init icoll_of_init(struct device_node *np,
struct device_node *interrupt_parent)
{
- icoll_base = of_iomap(np, 0);
- WARN_ON(!icoll_base);
+ void __iomem *icoll_base;
+
+ icoll_priv.type = ICOLL;
+
+ icoll_base = icoll_init_iobase(np);
+ icoll_priv.vector = icoll_base + HW_ICOLL_VECTOR;
+ icoll_priv.levelack = icoll_base + HW_ICOLL_LEVELACK;
+ icoll_priv.ctrl = icoll_base + HW_ICOLL_CTRL;
+ icoll_priv.stat = icoll_base + HW_ICOLL_STAT_OFFSET;
+ icoll_priv.intr = icoll_base + HW_ICOLL_INTERRUPT0;
+ icoll_priv.clear = NULL;
/*
* Interrupt Collector reset, which initializes the priority
* for each irq to level 0.
*/
- stmp_reset_block(icoll_base + HW_ICOLL_CTRL);
+ stmp_reset_block(icoll_priv.ctrl);
- icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
- &icoll_irq_domain_ops, NULL);
- return icoll_domain ? 0 : -ENODEV;
+ icoll_add_domain(np, ICOLL_NUM_IRQS);
+
+ return 0;
}
IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
+
+static int __init asm9260_of_init(struct device_node *np,
+ struct device_node *interrupt_parent)
+{
+ void __iomem *icoll_base;
+ int i;
+
+ icoll_priv.type = ASM9260_ICOLL;
+
+ icoll_base = icoll_init_iobase(np);
+ icoll_priv.vector = icoll_base + ASM9260_HW_ICOLL_VECTOR;
+ icoll_priv.levelack = icoll_base + ASM9260_HW_ICOLL_LEVELACK;
+ icoll_priv.ctrl = icoll_base + ASM9260_HW_ICOLL_CTRL;
+ icoll_priv.stat = icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
+ icoll_priv.intr = icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
+ icoll_priv.clear = icoll_base + ASM9260_HW_ICOLL_CLEAR0;
+
+ writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
+ icoll_priv.ctrl);
+ /*
+ * ASM9260 don't provide reset bit. So, we need to set level 0
+ * manually.
+ */
+ for (i = 0; i < 16 * 0x10; i += 0x10)
+ writel(0, icoll_priv.intr + i);
+
+ icoll_add_domain(np, ASM9260_NUM_IRQS);
+
+ return 0;
+}
+IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);
diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c
index a878b8d03..b1777104f 100644
--- a/drivers/irqchip/irq-nvic.c
+++ b/drivers/irqchip/irq-nvic.c
@@ -48,16 +48,26 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
handle_IRQ(irq, regs);
}
+static int nvic_irq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq, unsigned int *type)
+{
+ if (WARN_ON(fwspec->param_count < 1))
+ return -EINVAL;
+ *hwirq = fwspec->param[0];
+ *type = IRQ_TYPE_NONE;
+ return 0;
+}
+
static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
int i, ret;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
- struct of_phandle_args *irq_data = arg;
+ struct irq_fwspec *fwspec = arg;
- ret = irq_domain_xlate_onecell(domain, irq_data->np, irq_data->args,
- irq_data->args_count, &hwirq, &type);
+ ret = nvic_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
@@ -68,7 +78,7 @@ static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
}
static const struct irq_domain_ops nvic_irq_domain_ops = {
- .xlate = irq_domain_xlate_onecell,
+ .translate = nvic_irq_domain_translate,
.alloc = nvic_irq_domain_alloc,
.free = irq_domain_free_irqs_top,
};
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index 952533572..c32580656 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -361,14 +361,16 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
-static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a7779 = {
+static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = {
.irlm_bit = 23, /* ICR0.IRLM0 */
};
static const struct of_device_id intc_irqpin_dt_ids[] = {
{ .compatible = "renesas,intc-irqpin", },
+ { .compatible = "renesas,intc-irqpin-r8a7778",
+ .data = &intc_irqpin_irlm_r8a777x },
{ .compatible = "renesas,intc-irqpin-r8a7779",
- .data = &intc_irqpin_irlm_r8a7779 },
+ .data = &intc_irqpin_irlm_r8a777x },
{},
};
MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 35bf97ba4..52304b139 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -62,33 +62,20 @@ struct irqc_priv {
struct irqc_irq irq[IRQC_IRQ_MAX];
unsigned int number_of_irqs;
struct platform_device *pdev;
- struct irq_chip irq_chip;
+ struct irq_chip_generic *gc;
struct irq_domain *irq_domain;
struct clk *clk;
};
-static void irqc_dbg(struct irqc_irq *i, char *str)
-{
- dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
- str, i->requested_irq, i->hw_irq);
-}
-
-static void irqc_irq_enable(struct irq_data *d)
+static struct irqc_priv *irq_data_to_priv(struct irq_data *data)
{
- struct irqc_priv *p = irq_data_get_irq_chip_data(d);
- int hw_irq = irqd_to_hwirq(d);
-
- irqc_dbg(&p->irq[hw_irq], "enable");
- iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
+ return data->domain->host_data;
}
-static void irqc_irq_disable(struct irq_data *d)
+static void irqc_dbg(struct irqc_irq *i, char *str)
{
- struct irqc_priv *p = irq_data_get_irq_chip_data(d);
- int hw_irq = irqd_to_hwirq(d);
-
- irqc_dbg(&p->irq[hw_irq], "disable");
- iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
+ dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
+ str, i->requested_irq, i->hw_irq);
}
static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
@@ -101,7 +88,7 @@ static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
{
- struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+ struct irqc_priv *p = irq_data_to_priv(d);
int hw_irq = irqd_to_hwirq(d);
unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
u32 tmp;
@@ -120,7 +107,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
{
- struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+ struct irqc_priv *p = irq_data_to_priv(d);
int hw_irq = irqd_to_hwirq(d);
irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
@@ -153,35 +140,11 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
-/*
- * This lock class tells lockdep that IRQC irqs are in a different
- * category than their parents, so it won't report false recursion.
- */
-static struct lock_class_key irqc_irq_lock_class;
-
-static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- struct irqc_priv *p = h->host_data;
-
- irqc_dbg(&p->irq[hw], "map");
- irq_set_chip_data(virq, h->host_data);
- irq_set_lockdep_class(virq, &irqc_irq_lock_class);
- irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
- return 0;
-}
-
-static const struct irq_domain_ops irqc_irq_domain_ops = {
- .map = irqc_irq_domain_map,
- .xlate = irq_domain_xlate_twocell,
-};
-
static int irqc_probe(struct platform_device *pdev)
{
struct irqc_priv *p;
struct resource *io;
struct resource *irq;
- struct irq_chip *irq_chip;
const char *name = dev_name(&pdev->dev);
int ret;
int k;
@@ -241,40 +204,51 @@ static int irqc_probe(struct platform_device *pdev)
p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
- irq_chip = &p->irq_chip;
- irq_chip->name = name;
- irq_chip->irq_mask = irqc_irq_disable;
- irq_chip->irq_unmask = irqc_irq_enable;
- irq_chip->irq_set_type = irqc_irq_set_type;
- irq_chip->irq_set_wake = irqc_irq_set_wake;
- irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
-
p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
p->number_of_irqs,
- &irqc_irq_domain_ops, p);
+ &irq_generic_chip_ops, p);
if (!p->irq_domain) {
ret = -ENXIO;
dev_err(&pdev->dev, "cannot initialize irq domain\n");
goto err2;
}
+ ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs,
+ 1, name, handle_level_irq,
+ 0, 0, IRQ_GC_INIT_NESTED_LOCK);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot allocate generic chip\n");
+ goto err3;
+ }
+
+ p->gc = irq_get_domain_generic_chip(p->irq_domain, 0);
+ p->gc->reg_base = p->cpu_int_base;
+ p->gc->chip_types[0].regs.enable = IRQC_EN_SET;
+ p->gc->chip_types[0].regs.disable = IRQC_EN_STS;
+ p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+ p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
+ p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type;
+ p->gc->chip_types[0].chip.irq_set_wake = irqc_irq_set_wake;
+ p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
/* request interrupts one by one */
for (k = 0; k < p->number_of_irqs; k++) {
if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
0, name, &p->irq[k])) {
dev_err(&pdev->dev, "failed to request IRQ\n");
ret = -ENOENT;
- goto err3;
+ goto err4;
}
}
dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
return 0;
-err3:
+err4:
while (--k >= 0)
free_irq(p->irq[k].requested_irq, &p->irq[k]);
+err3:
irq_domain_remove(p->irq_domain);
err2:
iounmap(p->iomem);
diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c
index 7154b011d..c71914e8f 100644
--- a/drivers/irqchip/irq-s3c24xx.c
+++ b/drivers/irqchip/irq-s3c24xx.c
@@ -311,7 +311,7 @@ static void s3c_irq_demux(struct irq_desc *desc)
* and one big domain for the dt case where the subintc
* starts at hwirq number 32.
*/
- offset = (intc->domain->of_node) ? 32 : 0;
+ offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;
chained_irq_enter(chip, desc);
@@ -342,7 +342,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
return false;
/* non-dt machines use individual domains */
- if (!intc->domain->of_node)
+ if (!irq_domain_get_of_node(intc->domain))
intc_offset = 0;
/* We have a problem that the INTOFFSET register does not always
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c
index c143dd584..4ef178078 100644
--- a/drivers/irqchip/irq-sunxi-nmi.c
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -8,6 +8,9 @@
* warranty of any kind, whether express or implied.
*/
+#define DRV_NAME "sunxi-nmi"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/io.h>
@@ -96,8 +99,8 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
break;
default:
irq_gc_unlock(gc);
- pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n",
- __func__, data->irq);
+ pr_err("Cannot assign multiple trigger modes to IRQ %d.\n",
+ data->irq);
return -EBADR;
}
@@ -130,30 +133,29 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
if (!domain) {
- pr_err("%s: Could not register interrupt domain.\n", node->name);
+ pr_err("Could not register interrupt domain.\n");
return -ENOMEM;
}
- ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name,
+ ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
handle_fasteoi_irq, clr, 0,
IRQ_GC_INIT_MASK_CACHE);
if (ret) {
- pr_err("%s: Could not allocate generic interrupt chip.\n",
- node->name);
- goto fail_irqd_remove;
+ pr_err("Could not allocate generic interrupt chip.\n");
+ goto fail_irqd_remove;
}
irq = irq_of_parse_and_map(node, 0);
if (irq <= 0) {
- pr_err("%s: unable to parse irq\n", node->name);
+ pr_err("unable to parse irq\n");
ret = -EINVAL;
goto fail_irqd_remove;
}
gc = irq_get_domain_generic_chip(domain, 0);
- gc->reg_base = of_iomap(node, 0);
+ gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!gc->reg_base) {
- pr_err("%s: unable to map resource\n", node->name);
+ pr_err("unable to map resource\n");
ret = -ENOMEM;
goto fail_irqd_remove;
}
diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c
index fd88e6877..121ec3013 100644
--- a/drivers/irqchip/irq-tegra.c
+++ b/drivers/irqchip/irq-tegra.c
@@ -221,41 +221,43 @@ static struct irq_chip tegra_ictlr_chip = {
#endif
};
-static int tegra_ictlr_domain_xlate(struct irq_domain *domain,
- struct device_node *controller,
- const u32 *intspec,
- unsigned int intsize,
- unsigned long *out_hwirq,
- unsigned int *out_type)
+static int tegra_ictlr_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
{
- if (domain->of_node != controller)
- return -EINVAL; /* Shouldn't happen, really... */
- if (intsize != 3)
- return -EINVAL; /* Not GIC compliant */
- if (intspec[0] != GIC_SPI)
- return -EINVAL; /* No PPI should point to this domain */
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
- *out_hwirq = intspec[1];
- *out_type = intspec[2];
- return 0;
+ /* No PPI should point to this domain */
+ if (fwspec->param[0] != 0)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[1];
+ *type = fwspec->param[2];
+ return 0;
+ }
+
+ return -EINVAL;
}
static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs, void *data)
{
- struct of_phandle_args *args = data;
- struct of_phandle_args parent_args;
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
struct tegra_ictlr_info *info = domain->host_data;
irq_hw_number_t hwirq;
unsigned int i;
- if (args->args_count != 3)
+ if (fwspec->param_count != 3)
return -EINVAL; /* Not GIC compliant */
- if (args->args[0] != GIC_SPI)
+ if (fwspec->param[0] != GIC_SPI)
return -EINVAL; /* No PPI should point to this domain */
- hwirq = args->args[1];
+ hwirq = fwspec->param[1];
if (hwirq >= (num_ictlrs * 32))
return -EINVAL;
@@ -267,9 +269,10 @@ static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
info->base[ictlr]);
}
- parent_args = *args;
- parent_args.np = domain->parent->of_node;
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+ parent_fwspec = *fwspec;
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
}
static void tegra_ictlr_domain_free(struct irq_domain *domain,
@@ -285,9 +288,9 @@ static void tegra_ictlr_domain_free(struct irq_domain *domain,
}
static const struct irq_domain_ops tegra_ictlr_domain_ops = {
- .xlate = tegra_ictlr_domain_xlate,
- .alloc = tegra_ictlr_domain_alloc,
- .free = tegra_ictlr_domain_free,
+ .translate = tegra_ictlr_domain_translate,
+ .alloc = tegra_ictlr_domain_alloc,
+ .free = tegra_ictlr_domain_free,
};
static int __init tegra_ictlr_init(struct device_node *node,
diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 598ab3f0e..cadf104e3 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -210,7 +210,12 @@ int __init fpga_irq_of_init(struct device_node *node,
parent_irq = -1;
}
+#ifdef CONFIG_ARCH_VERSATILE
+ fpga_irq_init(base, node->name, IRQ_SIC_START, parent_irq, valid_mask,
+ node);
+#else
fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node);
+#endif
writel(clear_mask, base + IRQ_ENABLE_CLEAR);
writel(clear_mask, base + FIQ_ENABLE_CLEAR);
diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c
index 2c2255886..56b5e3cb9 100644
--- a/drivers/irqchip/irq-vf610-mscm-ir.c
+++ b/drivers/irqchip/irq-vf610-mscm-ir.c
@@ -130,35 +130,51 @@ static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int vi
{
int i;
irq_hw_number_t hwirq;
- struct of_phandle_args *irq_data = arg;
- struct of_phandle_args gic_data;
+ struct irq_fwspec *fwspec = arg;
+ struct irq_fwspec parent_fwspec;
- if (irq_data->args_count != 2)
+ if (!irq_domain_get_of_node(domain->parent))
return -EINVAL;
- hwirq = irq_data->args[0];
+ if (fwspec->param_count != 2)
+ return -EINVAL;
+
+ hwirq = fwspec->param[0];
for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&vf610_mscm_ir_irq_chip,
domain->host_data);
- gic_data.np = domain->parent->of_node;
+ parent_fwspec.fwnode = domain->parent->fwnode;
if (mscm_ir_data->is_nvic) {
- gic_data.args_count = 1;
- gic_data.args[0] = irq_data->args[0];
+ parent_fwspec.param_count = 1;
+ parent_fwspec.param[0] = fwspec->param[0];
} else {
- gic_data.args_count = 3;
- gic_data.args[0] = GIC_SPI;
- gic_data.args[1] = irq_data->args[0];
- gic_data.args[2] = irq_data->args[1];
+ parent_fwspec.param_count = 3;
+ parent_fwspec.param[0] = GIC_SPI;
+ parent_fwspec.param[1] = fwspec->param[0];
+ parent_fwspec.param[2] = fwspec->param[1];
}
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static int vf610_mscm_ir_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (WARN_ON(fwspec->param_count < 2))
+ return -EINVAL;
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ return 0;
}
static const struct irq_domain_ops mscm_irq_domain_ops = {
- .xlate = irq_domain_xlate_twocell,
+ .translate = vf610_mscm_ir_domain_translate,
.alloc = vf610_mscm_ir_domain_alloc,
.free = irq_domain_free_irqs_common,
};
@@ -205,7 +221,8 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
goto out_unmap;
}
- if (of_device_is_compatible(domain->parent->of_node, "arm,armv7m-nvic"))
+ if (of_device_is_compatible(irq_domain_get_of_node(domain->parent),
+ "arm,armv7m-nvic"))
mscm_ir_data->is_nvic = true;
cpu_pm_register_notifier(&mscm_ir_notifier_block);
diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c
index afd1af3df..2b35e68be 100644
--- a/drivers/irqchip/irqchip.c
+++ b/drivers/irqchip/irqchip.c
@@ -8,7 +8,7 @@
* warranty of any kind, whether express or implied.
*/
-#include <linux/acpi_irq.h>
+#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/of_irq.h>
#include <linux/irqchip.h>
@@ -27,6 +27,5 @@ extern struct of_device_id __irqchip_of_table[];
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
-
- acpi_irq_init();
+ acpi_probe_device_table(irqchip);
}