summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/irqchip
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/irqchip
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/irqchip')
-rw-r--r--kernel/drivers/irqchip/Kconfig35
-rw-r--r--kernel/drivers/irqchip/Makefile12
-rw-r--r--kernel/drivers/irqchip/alphascale_asm9260-icoll.h109
-rw-r--r--kernel/drivers/irqchip/exynos-combiner.c90
-rw-r--r--kernel/drivers/irqchip/irq-armada-370-xp.c28
-rw-r--r--kernel/drivers/irqchip/irq-atmel-aic-common.c4
-rw-r--r--kernel/drivers/irqchip/irq-atmel-aic.c4
-rw-r--r--kernel/drivers/irqchip/irq-atmel-aic5.c95
-rw-r--r--kernel/drivers/irqchip/irq-bcm2835.c115
-rw-r--r--kernel/drivers/irqchip/irq-bcm2836.c275
-rw-r--r--kernel/drivers/irqchip/irq-bcm7038-l1.c9
-rw-r--r--kernel/drivers/irqchip/irq-bcm7120-l2.c78
-rw-r--r--kernel/drivers/irqchip/irq-brcmstb-l2.c11
-rw-r--r--kernel/drivers/irqchip/irq-clps711x.c9
-rw-r--r--kernel/drivers/irqchip/irq-crossbar.c65
-rw-r--r--kernel/drivers/irqchip/irq-digicolor.c3
-rw-r--r--kernel/drivers/irqchip/irq-dw-apb-ictl.c58
-rw-r--r--kernel/drivers/irqchip/irq-gic-common.c41
-rw-r--r--kernel/drivers/irqchip/irq-gic-common.h9
-rw-r--r--kernel/drivers/irqchip/irq-gic-v2m.c183
-rw-r--r--kernel/drivers/irqchip/irq-gic-v3-its-pci-msi.c139
-rw-r--r--kernel/drivers/irqchip/irq-gic-v3-its-platform-msi.c106
-rw-r--r--kernel/drivers/irqchip/irq-gic-v3-its.c241
-rw-r--r--kernel/drivers/irqchip/irq-gic-v3.c258
-rw-r--r--kernel/drivers/irqchip/irq-gic.c456
-rw-r--r--kernel/drivers/irqchip/irq-hip04.c11
-rw-r--r--kernel/drivers/irqchip/irq-i8259.c384
-rw-r--r--kernel/drivers/irqchip/irq-imgpdc.c13
-rw-r--r--kernel/drivers/irqchip/irq-imx-gpcv2.c272
-rw-r--r--kernel/drivers/irqchip/irq-ingenic.c176
-rw-r--r--kernel/drivers/irqchip/irq-keystone.c13
-rw-r--r--kernel/drivers/irqchip/irq-metag-ext.c11
-rw-r--r--kernel/drivers/irqchip/irq-metag.c5
-rw-r--r--kernel/drivers/irqchip/irq-mips-cpu.c171
-rw-r--r--kernel/drivers/irqchip/irq-mips-gic.c204
-rw-r--r--kernel/drivers/irqchip/irq-mmp.c9
-rw-r--r--kernel/drivers/irqchip/irq-moxart.c3
-rw-r--r--kernel/drivers/irqchip/irq-mtk-sysirq.c56
-rw-r--r--kernel/drivers/irqchip/irq-mxs.c176
-rw-r--r--kernel/drivers/irqchip/irq-nvic.c41
-rw-r--r--kernel/drivers/irqchip/irq-omap-intc.c55
-rw-r--r--kernel/drivers/irqchip/irq-or1k-pic.c3
-rw-r--r--kernel/drivers/irqchip/irq-orion.c11
-rw-r--r--kernel/drivers/irqchip/irq-renesas-h8300h.c93
-rw-r--r--kernel/drivers/irqchip/irq-renesas-h8s.c101
-rw-r--r--kernel/drivers/irqchip/irq-renesas-intc-irqpin.c19
-rw-r--r--kernel/drivers/irqchip/irq-renesas-irqc.c107
-rw-r--r--kernel/drivers/irqchip/irq-s3c24xx.c33
-rw-r--r--kernel/drivers/irqchip/irq-sa11x0.c174
-rw-r--r--kernel/drivers/irqchip/irq-sirfsoc.c50
-rw-r--r--kernel/drivers/irqchip/irq-sun4i.c7
-rw-r--r--kernel/drivers/irqchip/irq-sunxi-nmi.c31
-rw-r--r--kernel/drivers/irqchip/irq-tb10x.c9
-rw-r--r--kernel/drivers/irqchip/irq-tegra.c59
-rw-r--r--kernel/drivers/irqchip/irq-versatile-fpga.c23
-rw-r--r--kernel/drivers/irqchip/irq-vf610-mscm-ir.c62
-rw-r--r--kernel/drivers/irqchip/irq-vic.c13
-rw-r--r--kernel/drivers/irqchip/irq-vt8500.c12
-rw-r--r--kernel/drivers/irqchip/irq-xtensa-mx.c3
-rw-r--r--kernel/drivers/irqchip/irq-xtensa-pic.c3
-rw-r--r--kernel/drivers/irqchip/irq-zevio.c3
-rw-r--r--kernel/drivers/irqchip/irqchip.c5
-rw-r--r--kernel/drivers/irqchip/irqchip.h28
-rw-r--r--kernel/drivers/irqchip/spear-shirq.c13
64 files changed, 3792 insertions, 1103 deletions
diff --git a/kernel/drivers/irqchip/Kconfig b/kernel/drivers/irqchip/Kconfig
index 6de62a96e..4d7294e5d 100644
--- a/kernel/drivers/irqchip/Kconfig
+++ b/kernel/drivers/irqchip/Kconfig
@@ -30,6 +30,7 @@ config ARM_GIC_V3_ITS
config ARM_NVIC
bool
select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_CHIP
config ARM_VIC
@@ -60,6 +61,10 @@ config ATMEL_AIC5_IRQ
select MULTI_IRQ_HANDLER
select SPARSE_IRQ
+config I8259
+ bool
+ select IRQ_DOMAIN
+
config BCM7038_L1_IRQ
bool
select GENERIC_IRQ_CHIP
@@ -85,6 +90,11 @@ config IMGPDC_IRQ
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
+config IRQ_MIPS_CPU
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config CLPS711X_IRQCHIP
bool
depends on ARCH_CLPS711X
@@ -113,6 +123,7 @@ config RENESAS_INTC_IRQPIN
config RENESAS_IRQC
bool
+ select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
config ST_IRQCHIP
@@ -158,3 +169,27 @@ config KEYSTONE_IRQ
config MIPS_GIC
bool
select MIPS_CM
+
+config INGENIC_IRQ
+ bool
+ depends on MACH_INGENIC
+ default y
+
+config RENESAS_H8300H_INTC
+ bool
+ select IRQ_DOMAIN
+
+config RENESAS_H8S_INTC
+ bool
+ select IRQ_DOMAIN
+
+config IMX_GPCV2
+ bool
+ 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/kernel/drivers/irqchip/Makefile b/kernel/drivers/irqchip/Makefile
index dda4927e4..177f78f6e 100644
--- a/kernel/drivers/irqchip/Makefile
+++ b/kernel/drivers/irqchip/Makefile
@@ -1,11 +1,12 @@
obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
+obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
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
@@ -22,12 +23,14 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
-obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o
+obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.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_I8259) += irq-i8259.o
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
+obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
@@ -47,3 +50,8 @@ obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
+obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
+obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
+obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
+obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
+obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
diff --git a/kernel/drivers/irqchip/alphascale_asm9260-icoll.h b/kernel/drivers/irqchip/alphascale_asm9260-icoll.h
new file mode 100644
index 000000000..5cec108ee
--- /dev/null
+++ b/kernel/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/kernel/drivers/irqchip/exynos-combiner.c b/kernel/drivers/irqchip/exynos-combiner.c
index 5945223b7..ead15be2d 100644
--- a/kernel/drivers/irqchip/exynos-combiner.c
+++ b/kernel/drivers/irqchip/exynos-combiner.c
@@ -13,14 +13,14 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/syscore_ops.h>
#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include "irqchip.h"
-
#define COMBINER_ENABLE_SET 0x0
#define COMBINER_ENABLE_CLEAR 0x4
#define COMBINER_INT_STATUS 0xC
@@ -34,9 +34,14 @@ struct combiner_chip_data {
unsigned int irq_mask;
void __iomem *base;
unsigned int parent_irq;
+#ifdef CONFIG_PM
+ u32 pm_save;
+#endif
};
+static struct combiner_chip_data *combiner_data;
static struct irq_domain *combiner_irq_domain;
+static unsigned int max_nr = 20;
static inline void __iomem *combiner_base(struct irq_data *data)
{
@@ -60,10 +65,10 @@ static void combiner_unmask_irq(struct irq_data *data)
__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET);
}
-static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
+static void combiner_handle_cascade_irq(struct irq_desc *desc)
{
- struct combiner_chip_data *chip_data = irq_get_handler_data(irq);
- struct irq_chip *chip = irq_get_chip(irq);
+ struct combiner_chip_data *chip_data = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int cascade_irq, combiner_irq;
unsigned long status;
@@ -81,7 +86,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))
- handle_bad_irq(irq, desc);
+ handle_bad_irq(desc);
else
generic_handle_irq(cascade_irq);
@@ -116,9 +121,8 @@ static struct irq_chip combiner_chip = {
static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
unsigned int irq)
{
- if (irq_set_handler_data(irq, combiner_data) != 0)
- BUG();
- irq_set_chained_handler(irq, combiner_handle_cascade_irq);
+ irq_set_chained_handler_and_data(irq, combiner_handle_cascade_irq,
+ combiner_data);
}
static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
@@ -140,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)
@@ -159,36 +163,34 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
irq_set_chip_data(irq, &combiner_data[hw >> 3]);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
return 0;
}
-static struct irq_domain_ops combiner_irq_domain_ops = {
+static const struct irq_domain_ops combiner_irq_domain_ops = {
.xlate = combiner_irq_domain_xlate,
.map = combiner_irq_domain_map,
};
static void __init combiner_init(void __iomem *combiner_base,
- struct device_node *np,
- unsigned int max_nr)
+ struct device_node *np)
{
int i, irq;
unsigned int nr_irq;
- struct combiner_chip_data *combiner_data;
nr_irq = max_nr * IRQ_IN_COMBINER;
combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
if (!combiner_data) {
- pr_warning("%s: could not allocate combiner data\n", __func__);
+ pr_warn("%s: could not allocate combiner data\n", __func__);
return;
}
combiner_irq_domain = irq_domain_add_linear(np, nr_irq,
&combiner_irq_domain_ops, combiner_data);
if (WARN_ON(!combiner_irq_domain)) {
- pr_warning("%s: irq domain init failed\n", __func__);
+ pr_warn("%s: irq domain init failed\n", __func__);
return;
}
@@ -201,11 +203,59 @@ static void __init combiner_init(void __iomem *combiner_base,
}
}
+#ifdef CONFIG_PM
+
+/**
+ * combiner_suspend - save interrupt combiner state before suspend
+ *
+ * Save the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state.
+ *
+ */
+static int combiner_suspend(void)
+{
+ int i;
+
+ for (i = 0; i < max_nr; i++)
+ combiner_data[i].pm_save =
+ __raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET);
+
+ return 0;
+}
+
+/**
+ * combiner_resume - restore interrupt combiner state after resume
+ *
+ * Restore the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state on suspend.
+ *
+ */
+static void combiner_resume(void)
+{
+ int i;
+
+ for (i = 0; i < max_nr; i++) {
+ __raw_writel(combiner_data[i].irq_mask,
+ combiner_data[i].base + COMBINER_ENABLE_CLEAR);
+ __raw_writel(combiner_data[i].pm_save,
+ combiner_data[i].base + COMBINER_ENABLE_SET);
+ }
+}
+
+#else
+#define combiner_suspend NULL
+#define combiner_resume NULL
+#endif
+
+static struct syscore_ops combiner_syscore_ops = {
+ .suspend = combiner_suspend,
+ .resume = combiner_resume,
+};
+
static int __init combiner_of_init(struct device_node *np,
struct device_node *parent)
{
void __iomem *combiner_base;
- unsigned int max_nr = 20;
combiner_base = of_iomap(np, 0);
if (!combiner_base) {
@@ -219,7 +269,9 @@ static int __init combiner_of_init(struct device_node *np,
__func__, max_nr);
}
- combiner_init(combiner_base, np, max_nr);
+ combiner_init(combiner_base, np);
+
+ register_syscore_ops(&combiner_syscore_ops);
return 0;
}
diff --git a/kernel/drivers/irqchip/irq-armada-370-xp.c b/kernel/drivers/irqchip/irq-armada-370-xp.c
index daccc8bdb..3f3a8c3d2 100644
--- a/kernel/drivers/irqchip/irq-armada-370-xp.c
+++ b/kernel/drivers/irqchip/irq-armada-370-xp.c
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/cpu.h>
#include <linux/io.h>
@@ -33,8 +34,6 @@
#include <asm/smp_plat.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
/* Interrupt Controller Registers Map */
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
@@ -57,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
@@ -82,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;
}
/*
@@ -201,7 +194,6 @@ static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
{
irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
handle_simple_irq);
- set_irq_flags(virq, IRQF_VALID);
return 0;
}
@@ -318,7 +310,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
handle_level_irq);
}
- set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(virq);
+ irq_clear_status_flags(virq, IRQ_NOAUTOEN);
return 0;
}
@@ -409,7 +402,7 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
};
#endif /* CONFIG_SMP */
-static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
.map = armada_370_xp_mpic_irq_map,
.xlate = irq_domain_xlate_onecell,
};
@@ -448,10 +441,9 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
#endif
-static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq,
- struct irq_desc *desc)
+static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc)
{
- struct irq_chip *chip = irq_get_chip(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned long irqmap, irqn, irqsrc, cpuid;
unsigned int cascade_irq;
@@ -552,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/kernel/drivers/irqchip/irq-atmel-aic-common.c b/kernel/drivers/irqchip/irq-atmel-aic-common.c
index 63cd031b2..37199b9b2 100644
--- a/kernel/drivers/irqchip/irq-atmel-aic-common.c
+++ b/kernel/drivers/irqchip/irq-atmel-aic-common.c
@@ -86,7 +86,7 @@ int aic_common_set_priority(int priority, unsigned *val)
priority > AT91_AIC_IRQ_MAX_PRIORITY)
return -EINVAL;
- *val &= AT91_AIC_PRIOR;
+ *val &= ~AT91_AIC_PRIOR;
*val |= priority;
return 0;
@@ -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/kernel/drivers/irqchip/irq-atmel-aic.c b/kernel/drivers/irqchip/irq-atmel-aic.c
index dae3604b3..8a0c7f288 100644
--- a/kernel/drivers/irqchip/irq-atmel-aic.c
+++ b/kernel/drivers/irqchip/irq-atmel-aic.c
@@ -19,6 +19,7 @@
#include <linux/bitmap.h>
#include <linux/types.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -31,7 +32,6 @@
#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
@@ -225,7 +225,7 @@ static void __init at91sam9g45_aic_irq_fixup(struct device_node *root)
aic_common_rtt_irq_fixup(root);
}
-static const struct of_device_id __initdata aic_irq_fixups[] = {
+static const struct of_device_id aic_irq_fixups[] __initconst = {
{ .compatible = "atmel,at91rm9200", .data = at91rm9200_aic_irq_fixup },
{ .compatible = "atmel,at91sam9g45", .data = at91sam9g45_aic_irq_fixup },
{ .compatible = "atmel,at91sam9n12", .data = at91rm9200_aic_irq_fixup },
diff --git a/kernel/drivers/irqchip/irq-atmel-aic5.c b/kernel/drivers/irqchip/irq-atmel-aic5.c
index a2e8c3f87..62bb840c6 100644
--- a/kernel/drivers/irqchip/irq-atmel-aic5.c
+++ b/kernel/drivers/irqchip/irq-atmel-aic5.c
@@ -19,6 +19,7 @@
#include <linux/bitmap.h>
#include <linux/types.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -31,7 +32,6 @@
#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
@@ -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,42 +86,47 @@ 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 *gc = 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);
- /* Disable interrupt on AIC5 */
- irq_gc_lock(gc);
+ /*
+ * Disable interrupt on AIC5. We always take the lock of the
+ * first irq chip as all chips share the same registers.
+ */
+ irq_gc_lock(bgc);
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
gc->mask_cache &= ~d->mask;
- irq_gc_unlock(gc);
+ irq_gc_unlock(bgc);
}
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];
+ struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- /* Enable interrupt on AIC5 */
- irq_gc_lock(gc);
+ /*
+ * Enable interrupt on AIC5. We always take the lock of the
+ * first irq chip as all chips share the same registers.
+ */
+ irq_gc_lock(bgc);
irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
irq_reg_writel(gc, 1, AT91_AIC5_IECR);
gc->mask_cache |= d->mask;
- irq_gc_unlock(gc);
+ irq_gc_unlock(bgc);
}
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;
}
@@ -130,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;
}
@@ -151,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;
@@ -175,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;
@@ -199,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;
@@ -254,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,
@@ -267,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;
}
@@ -290,7 +290,7 @@ 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[] = {
+static const struct of_device_id aic5_irq_fixups[] __initconst = {
{ .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
{ .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
{ /* sentinel */ },
@@ -339,6 +339,15 @@ static int __init aic5_of_init(struct device_node *node,
return 0;
}
+#define NR_SAMA5D2_IRQS 77
+
+static int __init sama5d2_aic5_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
+}
+IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
+
#define NR_SAMA5D3_IRQS 48
static int __init sama5d3_aic5_of_init(struct device_node *node,
diff --git a/kernel/drivers/irqchip/irq-bcm2835.c b/kernel/drivers/irqchip/irq-bcm2835.c
index 5916d6cda..bf9cc5f2e 100644
--- a/kernel/drivers/irqchip/irq-bcm2835.c
+++ b/kernel/drivers/irqchip/irq-bcm2835.c
@@ -48,13 +48,12 @@
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
/* Put the bank and irq (32 bits) into the hwirq */
#define MAKE_HWIRQ(b, n) ((b << 5) | (n))
#define HWIRQ_BANK(i) (i >> 5)
@@ -76,10 +75,10 @@
#define NR_BANKS 3
#define IRQS_PER_BANK 32
-static int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
-static int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
-static int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 };
-static int bank_irqs[] __initconst = { 8, 32, 32 };
+static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
+static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
+static const int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 };
+static const int bank_irqs[] __initconst = { 8, 32, 32 };
static const int shortcuts[] = {
7, 9, 10, 18, 19, /* Bank 1 */
@@ -97,6 +96,7 @@ struct armctrl_ic {
static struct armctrl_ic intc __read_mostly;
static void __exception_irq_entry bcm2835_handle_irq(
struct pt_regs *regs);
+static void bcm2836_chained_handle_irq(struct irq_desc *desc);
static void armctrl_mask_irq(struct irq_data *d)
{
@@ -135,12 +135,13 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
return 0;
}
-static struct irq_domain_ops armctrl_ops = {
+static const struct irq_domain_ops armctrl_ops = {
.xlate = armctrl_xlate
};
static int __init armctrl_of_init(struct device_node *node,
- struct device_node *parent)
+ struct device_node *parent,
+ bool is_2836)
{
void __iomem *base;
int irq, b, i;
@@ -165,58 +166,94 @@ static int __init armctrl_of_init(struct device_node *node,
BUG_ON(irq <= 0);
irq_set_chip_and_handler(irq, &armctrl_chip,
handle_level_irq);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
+ }
+ }
+
+ if (is_2836) {
+ int parent_irq = irq_of_parse_and_map(node, 0);
+
+ if (!parent_irq) {
+ panic("%s: unable to get parent interrupt.\n",
+ node->full_name);
}
+ irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq);
+ } else {
+ set_handle_irq(bcm2835_handle_irq);
}
- set_handle_irq(bcm2835_handle_irq);
return 0;
}
+static int __init bcm2835_armctrl_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return armctrl_of_init(node, parent, false);
+}
+
+static int __init bcm2836_armctrl_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return armctrl_of_init(node, parent, true);
+}
+
+
/*
* Handle each interrupt across the entire interrupt controller. This reads the
* status register before handling each interrupt, which is necessary given that
* handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
*/
-static void armctrl_handle_bank(int bank, struct pt_regs *regs)
+static u32 armctrl_translate_bank(int bank)
{
- u32 stat, irq;
+ u32 stat = readl_relaxed(intc.pending[bank]);
- while ((stat = readl_relaxed(intc.pending[bank]))) {
- irq = MAKE_HWIRQ(bank, ffs(stat) - 1);
- handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
- }
+ return MAKE_HWIRQ(bank, ffs(stat) - 1);
+}
+
+static u32 armctrl_translate_shortcut(int bank, u32 stat)
+{
+ return MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]);
}
-static void armctrl_handle_shortcut(int bank, struct pt_regs *regs,
- u32 stat)
+static u32 get_next_armctrl_hwirq(void)
{
- u32 irq = MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]);
- handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
+ u32 stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK;
+
+ if (stat == 0)
+ return ~0;
+ else if (stat & BANK0_HWIRQ_MASK)
+ return MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1);
+ else if (stat & SHORTCUT1_MASK)
+ return armctrl_translate_shortcut(1, stat & SHORTCUT1_MASK);
+ else if (stat & SHORTCUT2_MASK)
+ return armctrl_translate_shortcut(2, stat & SHORTCUT2_MASK);
+ else if (stat & BANK1_HWIRQ)
+ return armctrl_translate_bank(1);
+ else if (stat & BANK2_HWIRQ)
+ return armctrl_translate_bank(2);
+ else
+ BUG();
}
static void __exception_irq_entry bcm2835_handle_irq(
struct pt_regs *regs)
{
- u32 stat, irq;
-
- while ((stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK)) {
- if (stat & BANK0_HWIRQ_MASK) {
- irq = MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1);
- handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
- } else if (stat & SHORTCUT1_MASK) {
- armctrl_handle_shortcut(1, regs, stat & SHORTCUT1_MASK);
- } else if (stat & SHORTCUT2_MASK) {
- armctrl_handle_shortcut(2, regs, stat & SHORTCUT2_MASK);
- } else if (stat & BANK1_HWIRQ) {
- armctrl_handle_bank(1, regs);
- } else if (stat & BANK2_HWIRQ) {
- armctrl_handle_bank(2, regs);
- } else {
- BUG();
- }
- }
+ u32 hwirq;
+
+ while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+}
+
+static void bcm2836_chained_handle_irq(struct irq_desc *desc)
+{
+ u32 hwirq;
+
+ while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+ generic_handle_irq(irq_linear_revmap(intc.domain, hwirq));
}
-IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init);
+IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic",
+ bcm2835_armctrl_of_init);
+IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic",
+ bcm2836_armctrl_of_init);
diff --git a/kernel/drivers/irqchip/irq-bcm2836.c b/kernel/drivers/irqchip/irq-bcm2836.c
new file mode 100644
index 000000000..f68708281
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-bcm2836.c
@@ -0,0 +1,275 @@
+/*
+ * Root interrupt controller for the BCM2836 (Raspberry Pi 2).
+ *
+ * Copyright 2015 Broadcom
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/cpu.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <asm/exception.h>
+
+/*
+ * The low 2 bits identify the CPU that the GPU IRQ goes to, and the
+ * next 2 bits identify the CPU that the GPU FIQ goes to.
+ */
+#define LOCAL_GPU_ROUTING 0x00c
+/* When setting bits 0-3, enables PMU interrupts on that CPU. */
+#define LOCAL_PM_ROUTING_SET 0x010
+/* When setting bits 0-3, disables PMU interrupts on that CPU. */
+#define LOCAL_PM_ROUTING_CLR 0x014
+/*
+ * The low 4 bits of this are the CPU's timer IRQ enables, and the
+ * next 4 bits are the CPU's timer FIQ enables (which override the IRQ
+ * bits).
+ */
+#define LOCAL_TIMER_INT_CONTROL0 0x040
+/*
+ * The low 4 bits of this are the CPU's per-mailbox IRQ enables, and
+ * the next 4 bits are the CPU's per-mailbox FIQ enables (which
+ * override the IRQ bits).
+ */
+#define LOCAL_MAILBOX_INT_CONTROL0 0x050
+/*
+ * The CPU's interrupt status register. Bits are defined by the the
+ * LOCAL_IRQ_* bits below.
+ */
+#define LOCAL_IRQ_PENDING0 0x060
+/* Same status bits as above, but for FIQ. */
+#define LOCAL_FIQ_PENDING0 0x070
+/*
+ * Mailbox0 write-to-set bits. There are 16 mailboxes, 4 per CPU, and
+ * these bits are organized by mailbox number and then CPU number. We
+ * use mailbox 0 for IPIs. The mailbox's interrupt is raised while
+ * any bit is set.
+ */
+#define LOCAL_MAILBOX0_SET0 0x080
+/* Mailbox0 write-to-clear bits. */
+#define LOCAL_MAILBOX0_CLR0 0x0c0
+
+#define LOCAL_IRQ_CNTPSIRQ 0
+#define LOCAL_IRQ_CNTPNSIRQ 1
+#define LOCAL_IRQ_CNTHPIRQ 2
+#define LOCAL_IRQ_CNTVIRQ 3
+#define LOCAL_IRQ_MAILBOX0 4
+#define LOCAL_IRQ_MAILBOX1 5
+#define LOCAL_IRQ_MAILBOX2 6
+#define LOCAL_IRQ_MAILBOX3 7
+#define LOCAL_IRQ_GPU_FAST 8
+#define LOCAL_IRQ_PMU_FAST 9
+#define LAST_IRQ LOCAL_IRQ_PMU_FAST
+
+struct bcm2836_arm_irqchip_intc {
+ struct irq_domain *domain;
+ void __iomem *base;
+};
+
+static struct bcm2836_arm_irqchip_intc intc __read_mostly;
+
+static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
+ unsigned int bit,
+ int cpu)
+{
+ void __iomem *reg = intc.base + reg_offset + 4 * cpu;
+
+ writel(readl(reg) & ~BIT(bit), reg);
+}
+
+static void bcm2836_arm_irqchip_unmask_per_cpu_irq(unsigned int reg_offset,
+ unsigned int bit,
+ int cpu)
+{
+ void __iomem *reg = intc.base + reg_offset + 4 * cpu;
+
+ writel(readl(reg) | BIT(bit), reg);
+}
+
+static void bcm2836_arm_irqchip_mask_timer_irq(struct irq_data *d)
+{
+ bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
+ d->hwirq - LOCAL_IRQ_CNTPSIRQ,
+ smp_processor_id());
+}
+
+static void bcm2836_arm_irqchip_unmask_timer_irq(struct irq_data *d)
+{
+ bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
+ d->hwirq - LOCAL_IRQ_CNTPSIRQ,
+ smp_processor_id());
+}
+
+static struct irq_chip bcm2836_arm_irqchip_timer = {
+ .name = "bcm2836-timer",
+ .irq_mask = bcm2836_arm_irqchip_mask_timer_irq,
+ .irq_unmask = bcm2836_arm_irqchip_unmask_timer_irq,
+};
+
+static void bcm2836_arm_irqchip_mask_pmu_irq(struct irq_data *d)
+{
+ writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_CLR);
+}
+
+static void bcm2836_arm_irqchip_unmask_pmu_irq(struct irq_data *d)
+{
+ writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_SET);
+}
+
+static struct irq_chip bcm2836_arm_irqchip_pmu = {
+ .name = "bcm2836-pmu",
+ .irq_mask = bcm2836_arm_irqchip_mask_pmu_irq,
+ .irq_unmask = bcm2836_arm_irqchip_unmask_pmu_irq,
+};
+
+static void bcm2836_arm_irqchip_mask_gpu_irq(struct irq_data *d)
+{
+}
+
+static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip bcm2836_arm_irqchip_gpu = {
+ .name = "bcm2836-gpu",
+ .irq_mask = bcm2836_arm_irqchip_mask_gpu_irq,
+ .irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq,
+};
+
+static void bcm2836_arm_irqchip_register_irq(int hwirq, struct irq_chip *chip)
+{
+ int irq = irq_create_mapping(intc.domain, hwirq);
+
+ irq_set_percpu_devid(irq);
+ irq_set_chip_and_handler(irq, chip, handle_percpu_devid_irq);
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+}
+
+static void
+__exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+ u32 stat;
+
+ stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
+ if (stat & 0x10) {
+#ifdef CONFIG_SMP
+ void __iomem *mailbox0 = (intc.base +
+ LOCAL_MAILBOX0_CLR0 + 16 * cpu);
+ u32 mbox_val = readl(mailbox0);
+ u32 ipi = ffs(mbox_val) - 1;
+
+ writel(1 << ipi, mailbox0);
+ handle_IPI(ipi, regs);
+#endif
+ } else {
+ u32 hwirq = ffs(stat) - 1;
+
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+ }
+}
+
+#ifdef CONFIG_SMP
+static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
+ unsigned int ipi)
+{
+ int cpu;
+ void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
+
+ /*
+ * Ensure that stores to normal memory are visible to the
+ * other CPUs before issuing the IPI.
+ */
+ dsb();
+
+ for_each_cpu(cpu, mask) {
+ writel(1 << ipi, mailbox0_base + 16 * cpu);
+ }
+}
+
+/* Unmasks the IPI on the CPU when it's online. */
+static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0;
+ unsigned int mailbox = 0;
+
+ if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+ bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu);
+ else if (action == CPU_DYING)
+ bcm2836_arm_irqchip_mask_per_cpu_irq(int_reg, mailbox, cpu);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = {
+ .notifier_call = bcm2836_arm_irqchip_cpu_notify,
+ .priority = 100,
+};
+#endif
+
+static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
+ .xlate = irq_domain_xlate_onecell
+};
+
+static void
+bcm2836_arm_irqchip_smp_init(void)
+{
+#ifdef CONFIG_SMP
+ /* Unmask IPIs to the boot CPU. */
+ bcm2836_arm_irqchip_cpu_notify(&bcm2836_arm_irqchip_cpu_notifier,
+ CPU_STARTING,
+ (void *)smp_processor_id());
+ register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier);
+
+ set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
+#endif
+}
+
+static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ intc.base = of_iomap(node, 0);
+ if (!intc.base) {
+ panic("%s: unable to map local interrupt registers\n",
+ node->full_name);
+ }
+
+ intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
+ &bcm2836_arm_irqchip_intc_ops,
+ NULL);
+ if (!intc.domain)
+ panic("%s: unable to create IRQ domain\n", node->full_name);
+
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTPSIRQ,
+ &bcm2836_arm_irqchip_timer);
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTPNSIRQ,
+ &bcm2836_arm_irqchip_timer);
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTHPIRQ,
+ &bcm2836_arm_irqchip_timer);
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTVIRQ,
+ &bcm2836_arm_irqchip_timer);
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_GPU_FAST,
+ &bcm2836_arm_irqchip_gpu);
+ bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_PMU_FAST,
+ &bcm2836_arm_irqchip_pmu);
+
+ bcm2836_arm_irqchip_smp_init();
+
+ set_handle_irq(bcm2836_arm_irqchip_handle_irq);
+ return 0;
+}
+
+IRQCHIP_DECLARE(bcm2836_arm_irqchip_l1_intc, "brcm,bcm2836-l1-intc",
+ bcm2836_arm_irqchip_l1_intc_of_init);
diff --git a/kernel/drivers/irqchip/irq-bcm7038-l1.c b/kernel/drivers/irqchip/irq-bcm7038-l1.c
index d3b8c8be1..0fea985ef 100644
--- a/kernel/drivers/irqchip/irq-bcm7038-l1.c
+++ b/kernel/drivers/irqchip/irq-bcm7038-l1.c
@@ -29,10 +29,9 @@
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/types.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
-#include "irqchip.h"
-
#define IRQS_PER_WORD 32
#define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4)
#define MAX_WORDS 8
@@ -116,7 +115,7 @@ static inline void l1_writel(u32 val, void __iomem *reg)
writel(val, reg);
}
-static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc)
+static void bcm7038_l1_irq_handle(struct irq_desc *desc)
{
struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc);
struct bcm7038_l1_cpu *cpu;
@@ -257,8 +256,8 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
pr_err("failed to map parent interrupt %d\n", parent_irq);
return -EINVAL;
}
- irq_set_handler_data(parent_irq, intc);
- irq_set_chained_handler(parent_irq, bcm7038_l1_irq_handle);
+ irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle,
+ intc);
return 0;
}
diff --git a/kernel/drivers/irqchip/irq-bcm7120-l2.c b/kernel/drivers/irqchip/irq-bcm7120-l2.c
index 3ba5cc780..61b18ab33 100644
--- a/kernel/drivers/irqchip/irq-bcm7120-l2.c
+++ b/kernel/drivers/irqchip/irq-bcm7120-l2.c
@@ -26,10 +26,9 @@
#include <linux/irqdomain.h>
#include <linux/reboot.h>
#include <linux/bitops.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
-#include "irqchip.h"
-
/* Register offset in the L2 interrupt controller */
#define IRQEN 0x00
#define IRQSTAT 0x04
@@ -38,6 +37,11 @@
#define MAX_MAPPINGS (MAX_WORDS * 2)
#define IRQS_PER_WORD 32
+struct bcm7120_l1_intc_data {
+ struct bcm7120_l2_intc_data *b;
+ u32 irq_map_mask[MAX_WORDS];
+};
+
struct bcm7120_l2_intc_data {
unsigned int n_words;
void __iomem *map_base[MAX_MAPPINGS];
@@ -47,14 +51,15 @@ struct bcm7120_l2_intc_data {
struct irq_domain *domain;
bool can_wake;
u32 irq_fwd_mask[MAX_WORDS];
- u32 irq_map_mask[MAX_WORDS];
+ struct bcm7120_l1_intc_data *l1_data;
int num_parent_irqs;
const __be32 *map_mask_prop;
};
-static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
+static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc)
{
- struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
+ struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc);
+ struct bcm7120_l2_intc_data *b = data->b;
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int idx;
@@ -69,7 +74,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
irq_gc_lock(gc);
pending = irq_reg_readl(gc, b->stat_offset[idx]) &
- gc->mask_cache;
+ gc->mask_cache &
+ data->irq_map_mask[idx];
irq_gc_unlock(gc);
for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
@@ -81,11 +87,10 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
-static void bcm7120_l2_intc_suspend(struct irq_data *d)
+static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc)
{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct irq_chip_type *ct = irq_data_get_chip_type(d);
struct bcm7120_l2_intc_data *b = gc->private;
+ struct irq_chip_type *ct = gc->chip_types;
irq_gc_lock(gc);
if (b->can_wake)
@@ -94,10 +99,9 @@ static void bcm7120_l2_intc_suspend(struct irq_data *d)
irq_gc_unlock(gc);
}
-static void bcm7120_l2_intc_resume(struct irq_data *d)
+static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc)
{
- struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ struct irq_chip_type *ct = gc->chip_types;
/* Restore the saved mask */
irq_gc_lock(gc);
@@ -107,8 +111,9 @@ static void bcm7120_l2_intc_resume(struct irq_data *d)
static int bcm7120_l2_intc_init_one(struct device_node *dn,
struct bcm7120_l2_intc_data *data,
- int irq)
+ int irq, u32 *valid_mask)
{
+ struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq];
int parent_irq;
unsigned int idx;
@@ -120,20 +125,28 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
/* For multiple parent IRQs with multiple words, this looks like:
* <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
+ *
+ * We need to associate a given parent interrupt with its corresponding
+ * map_mask in order to mask the status register with it because we
+ * have the same handler being called for multiple parent interrupts.
+ *
+ * This is typically something needed on BCM7xxx (STB chips).
*/
for (idx = 0; idx < data->n_words; idx++) {
if (data->map_mask_prop) {
- data->irq_map_mask[idx] |=
+ l1_data->irq_map_mask[idx] |=
be32_to_cpup(data->map_mask_prop +
irq * data->n_words + idx);
} else {
- data->irq_map_mask[idx] = 0xffffffff;
+ l1_data->irq_map_mask[idx] = 0xffffffff;
}
+ valid_mask[idx] |= l1_data->irq_map_mask[idx];
}
- irq_set_handler_data(parent_irq, data);
- irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
+ l1_data->b = data;
+ irq_set_chained_handler_and_data(parent_irq,
+ bcm7120_l2_intc_irq_handle, l1_data);
return 0;
}
@@ -214,6 +227,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
struct irq_chip_type *ct;
int ret = 0;
unsigned int idx, irq, flags;
+ u32 valid_mask[MAX_WORDS] = { };
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -226,9 +240,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
goto out_unmap;
}
+ data->l1_data = kcalloc(data->num_parent_irqs, sizeof(*data->l1_data),
+ GFP_KERNEL);
+ if (!data->l1_data) {
+ ret = -ENOMEM;
+ goto out_free_l1_data;
+ }
+
ret = iomap_regs_fn(dn, data);
if (ret < 0)
- goto out_unmap;
+ goto out_free_l1_data;
for (idx = 0; idx < data->n_words; idx++) {
__raw_writel(data->irq_fwd_mask[idx],
@@ -237,16 +258,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
}
for (irq = 0; irq < data->num_parent_irqs; irq++) {
- ret = bcm7120_l2_intc_init_one(dn, data, irq);
+ ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask);
if (ret)
- goto out_unmap;
+ goto out_free_l1_data;
}
data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words,
&irq_generic_chip_ops, NULL);
if (!data->domain) {
ret = -ENOMEM;
- goto out_unmap;
+ goto out_free_l1_data;
}
/* MIPS chips strapped for BE will automagically configure the
@@ -270,7 +291,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
irq = idx * IRQS_PER_WORD;
gc = irq_get_domain_generic_chip(data->domain, irq);
- gc->unused = 0xffffffff & ~data->irq_map_mask[idx];
+ gc->unused = 0xffffffff & ~valid_mask[idx];
gc->private = data;
ct = gc->chip_types;
@@ -280,8 +301,15 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_ack = irq_gc_noop;
- ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
- ct->chip.irq_resume = bcm7120_l2_intc_resume;
+ gc->suspend = bcm7120_l2_intc_suspend;
+ gc->resume = bcm7120_l2_intc_resume;
+
+ /*
+ * Initialize mask-cache, in case we need it for
+ * saving/restoring fwd mask even w/o any child interrupts
+ * installed
+ */
+ gc->mask_cache = irq_reg_readl(gc, ct->regs.mask);
if (data->can_wake) {
/* This IRQ chip can wake the system, set all
@@ -300,6 +328,8 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
out_free_domain:
irq_domain_remove(data->domain);
+out_free_l1_data:
+ kfree(data->l1_data);
out_unmap:
for (idx = 0; idx < MAX_MAPPINGS; idx++) {
if (data->map_base[idx])
diff --git a/kernel/drivers/irqchip/irq-brcmstb-l2.c b/kernel/drivers/irqchip/irq-brcmstb-l2.c
index d6bcc6be0..65cd341f3 100644
--- a/kernel/drivers/irqchip/irq-brcmstb-l2.c
+++ b/kernel/drivers/irqchip/irq-brcmstb-l2.c
@@ -32,8 +32,6 @@
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
-#include "irqchip.h"
-
/* Register offsets in the L2 interrupt controller */
#define CPU_STATUS 0x00
#define CPU_SET 0x04
@@ -51,11 +49,12 @@ struct brcmstb_l2_intc_data {
u32 saved_mask; /* for suspend/resume */
};
-static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
+static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc)
{
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = irq_get_domain_generic_chip(b->domain, 0);
struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int irq;
u32 status;
chained_irq_enter(chip, desc);
@@ -65,7 +64,7 @@ static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
if (status == 0) {
raw_spin_lock(&desc->lock);
- handle_bad_irq(irq, desc);
+ handle_bad_irq(desc);
raw_spin_unlock(&desc->lock);
goto out;
}
@@ -172,8 +171,8 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
}
/* 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);
+ irq_set_chained_handler_and_data(data->parent_irq,
+ brcmstb_l2_intc_irq_handle, data);
gc = irq_get_domain_generic_chip(data->domain, 0);
gc->reg_base = data->base;
diff --git a/kernel/drivers/irqchip/irq-clps711x.c b/kernel/drivers/irqchip/irq-clps711x.c
index 33127f131..eb5eb0cd4 100644
--- a/kernel/drivers/irqchip/irq-clps711x.c
+++ b/kernel/drivers/irqchip/irq-clps711x.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -19,8 +20,6 @@
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
#define CLPS711X_INTSR1 (0x0240)
#define CLPS711X_INTMR1 (0x0280)
#define CLPS711X_BLEOI (0x0600)
@@ -133,14 +132,14 @@ 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;
+ unsigned int flags = 0;
if (!clps711x_irqs[hw].flags)
return 0;
if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
handler = handle_bad_irq;
- flags |= IRQF_NOAUTOEN;
+ flags |= IRQ_NOAUTOEN;
} else if (clps711x_irqs[hw].eoi) {
handler = handle_fasteoi_irq;
}
@@ -150,7 +149,7 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
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);
+ irq_modify_status(virq, IRQ_NOPROBE, flags);
return 0;
}
diff --git a/kernel/drivers/irqchip/irq-crossbar.c b/kernel/drivers/irqchip/irq-crossbar.c
index c12bb9333..75573fa43 100644
--- a/kernel/drivers/irqchip/irq-crossbar.c
+++ b/kernel/drivers/irqchip/irq-crossbar.c
@@ -11,13 +11,12 @@
*/
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
-#include "irqchip.h"
-
#define IRQ_FREE -1
#define IRQ_RESERVED -2
#define IRQ_SKIP -3
@@ -79,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) {
@@ -95,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
@@ -113,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 */
@@ -167,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/kernel/drivers/irqchip/irq-digicolor.c b/kernel/drivers/irqchip/irq-digicolor.c
index 3cbc658af..dad85e74c 100644
--- a/kernel/drivers/irqchip/irq-digicolor.c
+++ b/kernel/drivers/irqchip/irq-digicolor.c
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -20,8 +21,6 @@
#include <asm/exception.h>
-#include "irqchip.h"
-
#define UC_IRQ_CONTROL 0x04
#define IC_FLAG_CLEAR_LO 0x00
diff --git a/kernel/drivers/irqchip/irq-dw-apb-ictl.c b/kernel/drivers/irqchip/irq-dw-apb-ictl.c
index 53bb7326a..052f26636 100644
--- a/kernel/drivers/irqchip/irq-dw-apb-ictl.c
+++ b/kernel/drivers/irqchip/irq-dw-apb-ictl.c
@@ -13,36 +13,36 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include "irqchip.h"
-
#define APB_INT_ENABLE_L 0x00
#define APB_INT_ENABLE_H 0x04
#define APB_INT_MASK_L 0x08
#define APB_INT_MASK_H 0x0c
#define APB_INT_FINALSTATUS_L 0x30
#define APB_INT_FINALSTATUS_H 0x34
+#define APB_INT_BASE_OFFSET 0x04
-static void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc)
+static void dw_apb_ictl_handler(struct irq_desc *desc)
{
- struct irq_chip *chip = irq_get_chip(irq);
- struct irq_chip_generic *gc = irq_get_handler_data(irq);
- struct irq_domain *d = gc->private;
- u32 stat;
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
int n;
chained_irq_enter(chip, desc);
- for (n = 0; n < gc->num_ct; n++) {
- stat = readl_relaxed(gc->reg_base +
- APB_INT_FINALSTATUS_L + 4 * n);
+ for (n = 0; n < d->revmap_size; n += 32) {
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, n);
+ u32 stat = readl_relaxed(gc->reg_base + APB_INT_FINALSTATUS_L);
+
while (stat) {
u32 hwirq = ffs(stat) - 1;
- generic_handle_irq(irq_find_mapping(d,
- gc->irq_base + hwirq + 32 * n));
+ u32 virq = irq_find_mapping(d, gc->irq_base + hwirq);
+
+ generic_handle_irq(virq);
stat &= ~(1 << hwirq);
}
}
@@ -73,7 +73,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
struct irq_domain *domain;
struct irq_chip_generic *gc;
void __iomem *iobase;
- int ret, nrirqs, irq;
+ int ret, nrirqs, irq, i;
u32 reg;
/* Map the parent interrupt for the chained handler */
@@ -128,35 +128,25 @@ static int __init dw_apb_ictl_init(struct device_node *np,
goto err_unmap;
}
- ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1,
- np->name, handle_level_irq, clr, 0,
- IRQ_GC_MASK_CACHE_PER_TYPE |
+ ret = irq_alloc_domain_generic_chips(domain, 32, 1, np->name,
+ handle_level_irq, clr, 0,
IRQ_GC_INIT_MASK_CACHE);
if (ret) {
pr_err("%s: unable to alloc irq domain gc\n", np->full_name);
goto err_unmap;
}
- gc = irq_get_domain_generic_chip(domain, 0);
- gc->private = domain;
- gc->reg_base = iobase;
-
- gc->chip_types[0].regs.mask = APB_INT_MASK_L;
- gc->chip_types[0].regs.enable = APB_INT_ENABLE_L;
- gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
- gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
- gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
-
- if (nrirqs > 32) {
- gc->chip_types[1].regs.mask = APB_INT_MASK_H;
- gc->chip_types[1].regs.enable = APB_INT_ENABLE_H;
- gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit;
- gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit;
- gc->chip_types[1].chip.irq_resume = dw_apb_ictl_resume;
+ for (i = 0; i < DIV_ROUND_UP(nrirqs, 32); i++) {
+ gc = irq_get_domain_generic_chip(domain, i * 32);
+ gc->reg_base = iobase + i * APB_INT_BASE_OFFSET;
+ gc->chip_types[0].regs.mask = APB_INT_MASK_L;
+ gc->chip_types[0].regs.enable = APB_INT_ENABLE_L;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
}
- irq_set_handler_data(irq, gc);
- irq_set_chained_handler(irq, dw_apb_ictl_handler);
+ irq_set_chained_handler_and_data(irq, dw_apb_ictl_handler, domain);
return 0;
diff --git a/kernel/drivers/irqchip/irq-gic-common.c b/kernel/drivers/irqchip/irq-gic-common.c
index ad96ebb0c..f174ce0ca 100644
--- a/kernel/drivers/irqchip/irq-gic-common.c
+++ b/kernel/drivers/irqchip/irq-gic-common.c
@@ -21,14 +21,22 @@
#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))
{
- 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, oldval;
int ret = 0;
@@ -43,17 +51,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
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. If we tried to write a new configuration and failed,
* return an error.
@@ -62,9 +59,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
ret = -EINVAL;
- if (enabled)
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
-
if (sync_access)
sync_access();
@@ -90,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_ENABLE_CLEAR + i / 8);
+ base + GIC_DIST_ACTIVE_CLEAR + i / 8);
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ base + GIC_DIST_ENABLE_CLEAR + i / 8);
+ }
if (sync_access)
sync_access();
@@ -108,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/kernel/drivers/irqchip/irq-gic-common.h b/kernel/drivers/irqchip/irq-gic-common.h
index 35a988477..fff697db8 100644
--- a/kernel/drivers/irqchip/irq-gic-common.h
+++ b/kernel/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/kernel/drivers/irqchip/irq-gic-v2m.c b/kernel/drivers/irqchip/irq-gic-v2m.c
index fdf706555..87f8d104a 100644
--- a/kernel/drivers/irqchip/irq-gic-v2m.c
+++ b/kernel/drivers/irqchip/irq-gic-v2m.c
@@ -37,21 +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 msi_controller mchip;
+ 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 */
- struct irq_domain *domain;
+ u32 flags; /* v2m flags for specific implementation */
};
static void gicv2m_mask_msi_irq(struct irq_data *d)
@@ -97,9 +107,12 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS;
- msg->address_hi = (u32) (addr >> 32);
- msg->address_lo = (u32) (addr);
+ 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 = {
@@ -115,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;
@@ -145,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;
@@ -213,6 +233,69 @@ static bool is_msi_spi_valid(u32 base, u32 num)
return true;
}
+static struct irq_chip gicv2m_pmsi_irq_chip = {
+ .name = "pMSI",
+};
+
+static struct msi_domain_ops gicv2m_pmsi_ops = {
+};
+
+static struct msi_domain_info gicv2m_pmsi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+ .ops = &gicv2m_pmsi_ops,
+ .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)
{
@@ -225,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");
@@ -254,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) {
@@ -261,45 +358,13 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_iounmap;
}
- v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m);
- if (!v2m->domain) {
- pr_err("Failed to create GICv2m domain\n");
- ret = -ENOMEM;
- goto err_free_bm;
- }
-
- v2m->domain->parent = parent;
- v2m->mchip.of_node = node;
- v2m->mchip.domain = pci_msi_create_irq_domain(node,
- &gicv2m_msi_domain_info,
- v2m->domain);
- if (!v2m->mchip.domain) {
- pr_err("Failed to create MSI domain\n");
- ret = -ENOMEM;
- goto err_free_domains;
- }
-
- spin_lock_init(&v2m->msi_cnt_lock);
-
- ret = of_pci_msi_chip_add(&v2m->mchip);
- if (ret) {
- pr_err("Failed to add msi_chip.\n");
- goto err_free_domains;
- }
-
+ 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 (v2m->mchip.domain)
- irq_domain_remove(v2m->mchip.domain);
- if (v2m->domain)
- irq_domain_remove(v2m->domain);
-err_free_bm:
- kfree(v2m->bm);
err_iounmap:
iounmap(v2m->base);
err_free_v2m:
@@ -329,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/kernel/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/kernel/drivers/irqchip/irq-gic-v3-its-pci-msi.c
new file mode 100644
index 000000000..aee60ed02
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013-2015 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/msi.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+
+static void its_mask_msi_irq(struct irq_data *d)
+{
+ pci_msi_mask_irq(d);
+ irq_chip_mask_parent(d);
+}
+
+static void its_unmask_msi_irq(struct irq_data *d)
+{
+ pci_msi_unmask_irq(d);
+ irq_chip_unmask_parent(d);
+}
+
+static struct irq_chip its_msi_irq_chip = {
+ .name = "ITS-MSI",
+ .irq_unmask = its_unmask_msi_irq,
+ .irq_mask = its_mask_msi_irq,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_write_msi_msg = pci_msi_domain_write_msg,
+};
+
+struct its_pci_alias {
+ struct pci_dev *pdev;
+ u32 count;
+};
+
+static int its_pci_msi_vec_count(struct pci_dev *pdev)
+{
+ int msi, msix;
+
+ msi = max(pci_msi_vec_count(pdev), 0);
+ msix = max(pci_msix_vec_count(pdev), 0);
+
+ return max(msi, msix);
+}
+
+static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct its_pci_alias *dev_alias = data;
+
+ if (pdev != dev_alias->pdev)
+ dev_alias->count += its_pci_msi_vec_count(pdev);
+
+ return 0;
+}
+
+static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *info)
+{
+ struct pci_dev *pdev;
+ struct its_pci_alias dev_alias;
+ struct msi_domain_info *msi_info;
+
+ if (!dev_is_pci(dev))
+ return -EINVAL;
+
+ msi_info = msi_get_domain_info(domain->parent);
+
+ pdev = to_pci_dev(dev);
+ dev_alias.pdev = pdev;
+ dev_alias.count = nvec;
+
+ 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 = pci_msi_domain_get_msi_rid(domain, pdev);
+
+ return msi_info->ops->msi_prepare(domain->parent,
+ dev, dev_alias.count, info);
+}
+
+static struct msi_domain_ops its_pci_msi_ops = {
+ .msi_prepare = its_pci_msi_prepare,
+};
+
+static struct msi_domain_info its_pci_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
+ .ops = &its_pci_msi_ops,
+ .chip = &its_msi_irq_chip,
+};
+
+static struct of_device_id its_device_id[] = {
+ { .compatible = "arm,gic-v3-its", },
+ {},
+};
+
+static int __init its_pci_msi_init(void)
+{
+ struct device_node *np;
+ struct irq_domain *parent;
+
+ for (np = of_find_matching_node(NULL, its_device_id); np;
+ np = of_find_matching_node(np, its_device_id)) {
+ if (!of_property_read_bool(np, "msi-controller"))
+ continue;
+
+ parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("%s: unable to locate ITS domain\n",
+ np->full_name);
+ continue;
+ }
+
+ 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);
+ continue;
+ }
+
+ pr_info("PCI/MSI: %s domain created\n", np->full_name);
+ }
+
+ return 0;
+}
+early_initcall(its_pci_msi_init);
diff --git a/kernel/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/kernel/drivers/irqchip/irq-gic-v3-its-platform-msi.c
new file mode 100644
index 000000000..470b4aa7d
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-gic-v3-its-platform-msi.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013-2015 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/device.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+
+static struct irq_chip its_pmsi_irq_chip = {
+ .name = "ITS-pMSI",
+};
+
+static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *info)
+{
+ struct msi_domain_info *msi_info;
+ u32 dev_id;
+ int ret, index = 0;
+
+ msi_info = msi_get_domain_info(domain->parent);
+
+ /* Suck the DeviceID out of the msi-parent property */
+ 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;
+
+ /* ITS specific DeviceID, as the core ITS ignores dev. */
+ info->scratchpad[0].ul = dev_id;
+
+ return msi_info->ops->msi_prepare(domain->parent,
+ dev, nvec, info);
+}
+
+static struct msi_domain_ops its_pmsi_ops = {
+ .msi_prepare = its_pmsi_prepare,
+};
+
+static struct msi_domain_info its_pmsi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+ .ops = &its_pmsi_ops,
+ .chip = &its_pmsi_irq_chip,
+};
+
+static struct of_device_id its_device_id[] = {
+ { .compatible = "arm,gic-v3-its", },
+ {},
+};
+
+static int __init its_pmsi_init(void)
+{
+ struct device_node *np;
+ struct irq_domain *parent;
+
+ for (np = of_find_matching_node(NULL, its_device_id); np;
+ np = of_find_matching_node(np, its_device_id)) {
+ if (!of_property_read_bool(np, "msi-controller"))
+ continue;
+
+ parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("%s: unable to locate ITS domain\n",
+ np->full_name);
+ continue;
+ }
+
+ 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);
+ continue;
+ }
+
+ pr_info("Platform MSI: %s domain created\n", np->full_name);
+ }
+
+ return 0;
+}
+early_initcall(its_pmsi_init);
diff --git a/kernel/drivers/irqchip/irq-gic-v3-its.c b/kernel/drivers/irqchip/irq-gic-v3-its.c
index c00e2db35..a159529f9 100644
--- a/kernel/drivers/irqchip/irq-gic-v3-its.c
+++ b/kernel/drivers/irqchip/irq-gic-v3-its.c
@@ -30,15 +30,17 @@
#include <linux/percpu.h>
#include <linux/slab.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/exception.h>
-#include "irqchip.h"
+#include "irq-gic-common.h"
-#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0)
+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
+#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
@@ -54,14 +56,12 @@ struct its_collection {
/*
* The ITS structure - contains most of the infrastructure, with the
- * msi_controller, the command queue, the collections, and the list of
- * devices writing to it.
+ * top-level MSI domain, the command queue, the collections, and the
+ * list of devices writing to it.
*/
struct its_node {
raw_spinlock_t lock;
struct list_head entry;
- struct msi_controller msi_chip;
- struct irq_domain *domain;
void __iomem *base;
unsigned long phys_base;
struct its_cmd_block *cmd_base;
@@ -597,11 +597,6 @@ static void its_unmask_irq(struct irq_data *d)
lpi_set_config(d, true);
}
-static void its_eoi_irq(struct irq_data *d)
-{
- gic_write_eoir(d->hwirq);
-}
-
static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
@@ -638,31 +633,11 @@ static struct irq_chip its_irq_chip = {
.name = "ITS",
.irq_mask = its_mask_irq,
.irq_unmask = its_unmask_irq,
- .irq_eoi = its_eoi_irq,
+ .irq_eoi = irq_chip_eoi_parent,
.irq_set_affinity = its_set_affinity,
.irq_compose_msi_msg = its_irq_compose_msi_msg,
};
-static void its_mask_msi_irq(struct irq_data *d)
-{
- pci_msi_mask_irq(d);
- irq_chip_mask_parent(d);
-}
-
-static void its_unmask_msi_irq(struct irq_data *d)
-{
- pci_msi_unmask_irq(d);
- irq_chip_unmask_parent(d);
-}
-
-static struct irq_chip its_msi_irq_chip = {
- .name = "ITS-MSI",
- .irq_unmask = its_unmask_msi_irq,
- .irq_mask = its_mask_msi_irq,
- .irq_eoi = irq_chip_eoi_parent,
- .irq_write_msi_msg = pci_msi_domain_write_msg,
-};
-
/*
* How we allocate LPIs:
*
@@ -742,6 +717,9 @@ static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
out:
spin_unlock(&lpi_lock);
+ if (!bitmap)
+ *base = *nr_ids = 0;
+
return bitmap;
}
@@ -831,13 +809,28 @@ static void its_free_tables(struct its_node *its)
}
}
-static int its_alloc_tables(struct its_node *its)
+static int its_alloc_tables(const char *node_name, struct its_node *its)
{
int err;
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);
@@ -845,6 +838,7 @@ static int its_alloc_tables(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;
@@ -860,9 +854,6 @@ static int its_alloc_tables(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
@@ -874,11 +865,19 @@ static int its_alloc_tables(struct its_node *its)
if (order >= MAX_ORDER) {
order = MAX_ORDER - 1;
pr_warn("%s: Device Table too large, reduce its page order to %u\n",
- its->msi_chip.of_node->full_name, order);
+ node_name, order);
}
}
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;
@@ -907,7 +906,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);
@@ -921,8 +920,10 @@ retry_baser:
* non-cacheable as well.
*/
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
- if (!shr)
+ if (!shr) {
cache = GITS_BASER_nC;
+ __flush_dcache_area(base, alloc_size);
+ }
goto retry_baser;
}
@@ -944,7 +945,7 @@ retry_baser:
if (val != tmp) {
pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n",
- its->msi_chip.of_node->full_name, i,
+ node_name, i,
(unsigned long) val, (unsigned long) tmp);
err = -ENXIO;
goto out_free;
@@ -1163,6 +1164,8 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
return NULL;
}
+ __flush_dcache_area(itt, sz);
+
dev->its = its;
dev->itt = itt;
dev->nr_ites = nr_ites;
@@ -1209,98 +1212,67 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq)
return 0;
}
-struct its_pci_alias {
- struct pci_dev *pdev;
- u32 dev_id;
- u32 count;
-};
-
-static int its_pci_msi_vec_count(struct pci_dev *pdev)
-{
- int msi, msix;
-
- msi = max(pci_msi_vec_count(pdev), 0);
- msix = max(pci_msix_vec_count(pdev), 0);
-
- return max(msi, msix);
-}
-
-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(dev_alias->pdev);
-
- return 0;
-}
-
static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *info)
{
- struct pci_dev *pdev;
struct its_node *its;
struct its_device *its_dev;
- struct its_pci_alias dev_alias;
-
- if (!dev_is_pci(dev))
- return -EINVAL;
+ struct msi_domain_info *msi_info;
+ u32 dev_id;
- pdev = to_pci_dev(dev);
- dev_alias.pdev = pdev;
- dev_alias.count = nvec;
+ /*
+ * We ignore "dev" entierely, and rely on the dev_id that has
+ * been passed via the scratchpad. This limits this domain's
+ * usefulness to upper layers that definitely know that they
+ * are built on top of the ITS.
+ */
+ dev_id = info->scratchpad[0].ul;
- pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
- its = domain->parent->host_data;
+ msi_info = msi_get_domain_info(domain);
+ its = msi_info->data;
- its_dev = its_find_device(its, dev_alias.dev_id);
+ its_dev = its_find_device(its, dev_id);
if (its_dev) {
/*
* We already have seen this ID, probably through
* another alias (PCI bridge of some sort). No need to
* create the device.
*/
- dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id);
+ pr_debug("Reusing ITT for devID %x\n", dev_id);
goto out;
}
- its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count);
+ its_dev = its_create_device(its, dev_id, nvec);
if (!its_dev)
return -ENOMEM;
- dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n",
- dev_alias.count, ilog2(dev_alias.count));
+ pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
out:
info->scratchpad[0].ptr = its_dev;
- info->scratchpad[1].ptr = dev;
return 0;
}
-static struct msi_domain_ops its_pci_msi_ops = {
+static struct msi_domain_ops its_msi_domain_ops = {
.msi_prepare = its_msi_prepare,
};
-static struct msi_domain_info its_pci_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
- .ops = &its_pci_msi_ops,
- .chip = &its_msi_irq_chip,
-};
-
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,
@@ -1323,9 +1295,9 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
irq_domain_set_hwirq_and_chip(domain, virq + i,
hwirq, &its_irq_chip, its_dev);
- dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n",
- (int)(hwirq - its_dev->event_map.lpi_base),
- (int)hwirq, virq + i);
+ pr_debug("ID:%d pID:%d vID:%d\n",
+ (int)(hwirq - its_dev->event_map.lpi_base),
+ (int) hwirq, virq + i);
}
return 0;
@@ -1421,11 +1393,39 @@ 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;
struct its_node *its;
void __iomem *its_base;
+ struct irq_domain *inner_domain;
u32 val;
u64 baser, tmp;
int err;
@@ -1469,7 +1469,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
INIT_LIST_HEAD(&its->its_device_list);
its->base = its_base;
its->phys_base = res.start;
- its->msi_chip.of_node = node;
its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL);
@@ -1479,7 +1478,9 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
}
its->cmd_write = its->cmd_base;
- err = its_alloc_tables(its);
+ its_enable_quirks(its);
+
+ err = its_alloc_tables(node->full_name, its);
if (err)
goto out_free_cmd;
@@ -1515,26 +1516,27 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
writeq_relaxed(0, its->base + GITS_CWRITER);
writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
- if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
- its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
- if (!its->domain) {
+ if (of_property_read_bool(node, "msi-controller")) {
+ struct msi_domain_info *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
err = -ENOMEM;
goto out_free_tables;
}
- its->domain->parent = parent;
-
- its->msi_chip.domain = pci_msi_create_irq_domain(node,
- &its_pci_msi_domain_info,
- its->domain);
- if (!its->msi_chip.domain) {
+ inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
+ if (!inner_domain) {
err = -ENOMEM;
- goto out_free_domains;
+ kfree(info);
+ goto out_free_tables;
}
- err = of_pci_msi_chip_add(&its->msi_chip);
- if (err)
- goto out_free_domains;
+ inner_domain->parent = parent;
+ inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+ info->ops = &its_msi_domain_ops;
+ info->data = its;
+ inner_domain->host_data = info;
}
spin_lock(&its_lock);
@@ -1543,11 +1545,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
return 0;
-out_free_domains:
- if (its->msi_chip.domain)
- irq_domain_remove(its->msi_chip.domain);
- if (its->domain)
- irq_domain_remove(its->domain);
out_free_tables:
its_free_tables(its);
out_free_cmd:
diff --git a/kernel/drivers/irqchip/irq-gic-v3.c b/kernel/drivers/irqchip/irq-gic-v3.c
index 49875adb6..d7be6ddc3 100644
--- a/kernel/drivers/irqchip/irq-gic-v3.c
+++ b/kernel/drivers/irqchip/irq-gic-v3.c
@@ -25,14 +25,15 @@
#include <linux/percpu.h>
#include <linux/slab.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <asm/cputype.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>
+#include <asm/virt.h>
#include "irq-gic-common.h"
-#include "irqchip.h"
struct redist_region {
void __iomem *redist_base;
@@ -50,6 +51,7 @@ struct gic_chip_data {
};
static struct gic_chip_data gic_data __read_mostly;
+static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
@@ -106,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();
-}
-
-static void __maybe_unused gic_write_sgi1r(u64 val)
-{
- asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
-}
+#ifdef CONFIG_ARM64
+static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
-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)
{
@@ -231,6 +193,21 @@ static void gic_mask_irq(struct irq_data *d)
gic_poke_irq(d, GICD_ICENABLER);
}
+static void gic_eoimode1_mask_irq(struct irq_data *d)
+{
+ gic_mask_irq(d);
+ /*
+ * When masking a forwarded interrupt, make sure it is
+ * deactivated as well.
+ *
+ * This ensures that an interrupt that is getting
+ * disabled/masked will not get "stuck", because there is
+ * noone to deactivate it (guest is being terminated).
+ */
+ if (irqd_is_forwarded_to_vcpu(d))
+ gic_poke_irq(d, GICD_ICACTIVER);
+}
+
static void gic_unmask_irq(struct irq_data *d)
{
gic_poke_irq(d, GICD_ISENABLER);
@@ -296,6 +273,17 @@ static void gic_eoi_irq(struct irq_data *d)
gic_write_eoir(gic_irq(d));
}
+static void gic_eoimode1_eoi_irq(struct irq_data *d)
+{
+ /*
+ * No need to deactivate an LPI, or an interrupt that
+ * is is getting forwarded to a vcpu.
+ */
+ if (gic_irq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d))
+ return;
+ gic_write_dir(gic_irq(d));
+}
+
static int gic_set_type(struct irq_data *d, unsigned int type)
{
unsigned int irq = gic_irq(d);
@@ -322,11 +310,20 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
return gic_configure_irq(irq, type, base, rwp_wait);
}
-static u64 gic_mpidr_to_affinity(u64 mpidr)
+static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
+{
+ if (vcpu)
+ irqd_set_forwarded_to_vcpu(d);
+ else
+ irqd_clr_forwarded_to_vcpu(d);
+ return 0;
+}
+
+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));
@@ -336,22 +333,33 @@ 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();
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
int err;
+
+ if (static_key_true(&supports_deactivate))
+ gic_write_eoir(irqnr);
+
err = handle_domain_irq(gic_data.domain, irqnr, regs);
if (err) {
WARN_ONCE(true, "Unexpected interrupt received!\n");
- gic_write_eoir(irqnr);
+ if (static_key_true(&supports_deactivate)) {
+ if (irqnr < 8192)
+ gic_write_dir(irqnr);
+ } else {
+ gic_write_eoir(irqnr);
+ }
}
continue;
}
if (irqnr < 16) {
gic_write_eoir(irqnr);
+ if (static_key_true(&supports_deactivate))
+ gic_write_dir(irqnr);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#else
@@ -384,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;
@@ -415,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;
}
@@ -438,21 +445,33 @@ 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);
- /* EOI deactivates interrupt too (mode 0) */
- gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+ if (static_key_true(&supports_deactivate)) {
+ /* EOI drops priority only (mode 1) */
+ gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop);
+ } else {
+ /* 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);
@@ -504,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) {
@@ -568,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);
@@ -604,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,
@@ -658,6 +677,20 @@ static struct irq_chip gic_chip = {
.irq_set_affinity = gic_set_affinity,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static struct irq_chip gic_eoimode1_chip = {
+ .name = "GICv3",
+ .irq_mask = gic_eoimode1_mask_irq,
+ .irq_unmask = gic_unmask_irq,
+ .irq_eoi = gic_eoimode1_eoi_irq,
+ .irq_set_type = gic_set_type,
+ .irq_set_affinity = gic_set_affinity,
+ .irq_get_irqchip_state = gic_irq_get_irqchip_state,
+ .irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
};
#define GIC_ID_NR (1U << gic_data.rdists.id_bits)
@@ -665,6 +698,11 @@ static struct irq_chip gic_chip = {
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
+ struct irq_chip *chip = &gic_chip;
+
+ if (static_key_true(&supports_deactivate))
+ chip = &gic_eoimode1_chip;
+
/* SGIs are private to the core kernel */
if (hw < 16)
return -EPERM;
@@ -678,54 +716,55 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
/* PPIs */
if (hw < 32) {
irq_set_percpu_devid(irq);
- irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
- set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
}
/* SPIs */
if (hw >= 32 && hw < gic_data.irq_nr) {
- irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
}
/* LPIs */
if (hw >= 8192 && hw < GIC_ID_NR) {
if (!gic_dist_supports_lpis())
return -EPERM;
- irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
- set_irq_flags(irq, IRQF_VALID);
}
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,
@@ -734,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;
@@ -760,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;
@@ -819,11 +865,19 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
redist_stride = 0;
+ if (!is_hyp_mode_available())
+ static_key_slow_dec(&supports_deactivate);
+
+ if (static_key_true(&supports_deactivate))
+ pr_info("GIC: Using split EOI/Deactivate mode\n");
+
gic_data.dist_base = dist_base;
gic_data.redist_regions = rdist_regs;
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/kernel/drivers/irqchip/irq-gic.c b/kernel/drivers/irqchip/irq-gic.c
index 01999d74b..abf2ffaed 100644
--- a/kernel/drivers/irqchip/irq-gic.c
+++ b/kernel/drivers/irqchip/irq-gic.c
@@ -38,17 +38,30 @@
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/slab.h>
+#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>
#include <asm/exception.h>
#include <asm/smp_plat.h>
+#include <asm/virt.h>
#include "irq-gic-common.h"
-#include "irqchip.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;
@@ -60,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;
@@ -82,6 +97,8 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock);
#define NR_GIC_CPU_IF 8
static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
+static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
+
#ifndef MAX_GIC_NR
#define MAX_GIC_NR 1
#endif
@@ -137,6 +154,17 @@ static inline unsigned int gic_irq(struct irq_data *d)
return d->hwirq;
}
+static inline bool cascading_gic_irq(struct irq_data *d)
+{
+ void *data = irq_data_get_irq_handler_data(d);
+
+ /*
+ * If handler_data is set, this is a cascading interrupt, and
+ * it cannot possibly be forwarded.
+ */
+ return data != NULL;
+}
+
/*
* Routines to acknowledge, disable and enable interrupts
*/
@@ -157,6 +185,21 @@ static void gic_mask_irq(struct irq_data *d)
gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
}
+static void gic_eoimode1_mask_irq(struct irq_data *d)
+{
+ gic_mask_irq(d);
+ /*
+ * When masking a forwarded interrupt, make sure it is
+ * deactivated as well.
+ *
+ * This ensures that an interrupt that is getting
+ * disabled/masked will not get "stuck", because there is
+ * noone to deactivate it (guest is being terminated).
+ */
+ if (irqd_is_forwarded_to_vcpu(d))
+ gic_poke_irq(d, GIC_DIST_ACTIVE_CLEAR);
+}
+
static void gic_unmask_irq(struct irq_data *d)
{
gic_poke_irq(d, GIC_DIST_ENABLE_SET);
@@ -167,6 +210,15 @@ static void gic_eoi_irq(struct irq_data *d)
writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
}
+static void gic_eoimode1_eoi_irq(struct irq_data *d)
+{
+ /* Do not deactivate an IRQ forwarded to a vcpu. */
+ if (irqd_is_forwarded_to_vcpu(d))
+ return;
+
+ writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_DEACTIVATE);
+}
+
static int gic_irq_set_irqchip_state(struct irq_data *d,
enum irqchip_irq_state which, bool val)
{
@@ -233,6 +285,19 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
return gic_configure_irq(gicirq, type, base, NULL);
}
+static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
+{
+ /* Only interrupts on the primary GIC can be forwarded to a vcpu. */
+ if (cascading_gic_irq(d))
+ return -EINVAL;
+
+ if (vcpu)
+ irqd_set_forwarded_to_vcpu(d);
+ else
+ irqd_clr_forwarded_to_vcpu(d);
+ return 0;
+}
+
#ifdef CONFIG_SMP
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
@@ -272,11 +337,15 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr < 1021)) {
+ if (static_key_true(&supports_deactivate))
+ writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
handle_domain_irq(gic->domain, irqnr, regs);
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
+ if (static_key_true(&supports_deactivate))
+ writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
@@ -286,10 +355,10 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
} while (1);
}
-static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
+static void gic_handle_cascade_irq(struct irq_desc *desc)
{
- struct gic_chip_data *chip_data = irq_get_handler_data(irq);
- struct irq_chip *chip = irq_get_chip(irq);
+ struct gic_chip_data *chip_data = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int cascade_irq, gic_irq;
unsigned long status;
@@ -305,7 +374,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
cascade_irq = irq_find_mapping(chip_data->domain, gic_irq);
if (unlikely(gic_irq < 32 || gic_irq > 1020))
- handle_bad_irq(cascade_irq, desc);
+ handle_bad_irq(desc);
else
generic_handle_irq(cascade_irq);
@@ -324,15 +393,34 @@ static struct irq_chip gic_chip = {
#endif
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .flags = IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE |
+ IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static struct irq_chip gic_eoimode1_chip = {
+ .name = "GICv2",
+ .irq_mask = gic_eoimode1_mask_irq,
+ .irq_unmask = gic_unmask_irq,
+ .irq_eoi = gic_eoimode1_eoi_irq,
+ .irq_set_type = gic_set_type,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = gic_set_affinity,
+#endif
+ .irq_get_irqchip_state = gic_irq_get_irqchip_state,
+ .irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
+ .flags = IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE |
+ IRQCHIP_MASK_ON_SUSPEND,
};
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
{
if (gic_nr >= MAX_GIC_NR)
BUG();
- if (irq_set_handler_data(irq, &gic_data[gic_nr]) != 0)
- BUG();
- irq_set_chained_handler(irq, gic_handle_cascade_irq);
+ irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq,
+ &gic_data[gic_nr]);
}
static u8 gic_get_cpumask(struct gic_chip_data *gic)
@@ -354,10 +442,14 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
return mask;
}
-static void gic_cpu_if_up(void)
+static void gic_cpu_if_up(struct gic_chip_data *gic)
{
- void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
+ void __iomem *cpu_base = gic_data_cpu_base(gic);
u32 bypass = 0;
+ u32 mode = 0;
+
+ if (static_key_true(&supports_deactivate))
+ mode = GIC_CPU_CTRL_EOImodeNS;
/*
* Preserve bypass disable bits to be written back later
@@ -365,7 +457,7 @@ static void gic_cpu_if_up(void)
bypass = readl(cpu_base + GIC_CPU_CTRL);
bypass &= GICC_DIS_BYPASS_MASK;
- writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
+ writel_relaxed(bypass | mode | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
}
@@ -400,34 +492,47 @@ static void gic_cpu_init(struct gic_chip_data *gic)
int i;
/*
- * Get what the GIC says our CPU mask is.
+ * Setting up the CPU map is only relevant for the primary GIC
+ * because any nested/secondary GICs do not directly interface
+ * with the CPU(s).
*/
- BUG_ON(cpu >= NR_GIC_CPU_IF);
- cpu_mask = gic_get_cpumask(gic);
- gic_cpu_map[cpu] = cpu_mask;
+ if (gic == &gic_data[0]) {
+ /*
+ * Get what the GIC says our CPU mask is.
+ */
+ BUG_ON(cpu >= NR_GIC_CPU_IF);
+ cpu_mask = gic_get_cpumask(gic);
+ gic_cpu_map[cpu] = cpu_mask;
- /*
- * Clear our mask from the other map entries in case they're
- * still undefined.
- */
- for (i = 0; i < NR_GIC_CPU_IF; i++)
- if (i != cpu)
- gic_cpu_map[i] &= ~cpu_mask;
+ /*
+ * Clear our mask from the other map entries in case they're
+ * still undefined.
+ */
+ for (i = 0; i < NR_GIC_CPU_IF; i++)
+ if (i != cpu)
+ gic_cpu_map[i] &= ~cpu_mask;
+ }
gic_cpu_config(dist_base, NULL);
writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
- gic_cpu_if_up();
+ gic_cpu_if_up(gic);
}
-void gic_cpu_if_down(void)
+int gic_cpu_if_down(unsigned int gic_nr)
{
- void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
+ void __iomem *cpu_base;
u32 val = 0;
+ if (gic_nr >= MAX_GIC_NR)
+ return -EINVAL;
+
+ cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
val = readl(cpu_base + GIC_CPU_CTRL);
val &= ~GICC_ENABLE;
writel_relaxed(val, cpu_base + GIC_CPU_CTRL);
+
+ return 0;
}
#ifdef CONFIG_CPU_PM
@@ -463,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);
}
/*
@@ -501,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);
}
@@ -528,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);
@@ -551,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++)
@@ -563,7 +696,7 @@ static void gic_cpu_restore(unsigned int gic_nr)
dist_base + GIC_DIST_PRI + i * 4);
writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK);
- gic_cpu_if_up();
+ gic_cpu_if_up(&gic_data[gic_nr]);
}
static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
@@ -607,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);
@@ -788,15 +925,22 @@ void __init gic_init_physaddr(struct device_node *node)
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
+ struct irq_chip *chip = &gic_chip;
+
+ if (static_key_true(&supports_deactivate)) {
+ if (d->host_data == (void *)&gic_data[0])
+ chip = &gic_eoimode1_chip;
+ }
+
if (hw < 32) {
irq_set_percpu_devid(irq);
- irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
- set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else {
- irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
}
return 0;
}
@@ -805,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;
+
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
- /* For SPIs, we need to add 16 more to get the GIC irq ID number */
- if (!intspec[0])
- *out_hwirq += 16;
+ if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+ if(fwspec->param_count != 2)
+ return -EINVAL;
- *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1];
+ return 0;
+ }
- return ret;
+ return -EINVAL;
}
#ifdef CONFIG_SMP
@@ -854,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;
@@ -868,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,
};
@@ -876,17 +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,
};
-void gic_set_irqchip_flags(unsigned long flags)
-{
- gic_chip.flags |= flags;
-}
-
-void __init gic_init_bases(unsigned int gic_nr, int irq_start,
+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;
@@ -894,6 +1042,8 @@ 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... */
@@ -929,13 +1079,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
}
/*
- * Initialize the CPU interface map to all CPUs.
- * It will be refined as each CPU probes its ID.
- */
- for (i = 0; i < NR_GIC_CPU_IF; i++)
- gic_cpu_map[i] = 0xff;
-
- /*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
*/
@@ -945,11 +1088,11 @@ 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.
@@ -972,7 +1115,7 @@ 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);
}
@@ -980,11 +1123,20 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
return;
if (gic_nr == 0) {
+ /*
+ * Initialize the CPU interface map to all CPUs.
+ * It will be refined as each CPU probes its ID.
+ * This is only necessary for the primary GIC.
+ */
+ for (i = 0; i < NR_GIC_CPU_IF; i++)
+ gic_cpu_map[i] = 0xff;
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
register_cpu_notifier(&gic_cpu_notifier);
#endif
set_handle_irq(gic_handle_irq);
+ if (static_key_true(&supports_deactivate))
+ pr_info("GIC: Using split EOI/Deactivate mode\n");
}
gic_dist_init(gic);
@@ -992,9 +1144,58 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic_pm_init(gic);
}
+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, 0, NULL);
+}
+
#ifdef CONFIG_OF
static int gic_cnt __initdata;
+static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
+{
+ struct resource cpuif_res;
+
+ of_address_to_resource(node, 1, &cpuif_res);
+
+ if (!is_hyp_mode_available())
+ return false;
+ if (resource_size(&cpuif_res) < SZ_8K)
+ return false;
+ if (resource_size(&cpuif_res) == SZ_128K) {
+ u32 val_low, val_high;
+
+ /*
+ * Verify that we have the first 4kB of a GIC400
+ * aliased over the first 64kB by checking the
+ * GICC_IIDR register on both ends.
+ */
+ val_low = readl_relaxed(*base + GIC_CPU_IDENT);
+ val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000);
+ if ((val_low & 0xffff0fff) != 0x0202043B ||
+ val_low != val_high)
+ return false;
+
+ /*
+ * Move the base up by 60kB, so that we have a 8kB
+ * contiguous region, which allows us to use GICC_DIR
+ * at its normal offset. Please pass me that bucket.
+ */
+ *base += 0xf000;
+ cpuif_res.start += 0xf000;
+ pr_warn("GIC: Adjusting CPU interface base to %pa",
+ &cpuif_res.start);
+ }
+
+ return true;
+}
+
static int __init
gic_of_init(struct device_node *node, struct device_node *parent)
{
@@ -1012,10 +1213,18 @@ gic_of_init(struct device_node *node, struct device_node *parent)
cpu_base = of_iomap(node, 1);
WARN(!cpu_base, "unable to map gic cpu registers\n");
+ /*
+ * Disable split EOI/Deactivate if either HYP is not available
+ * or the CPU interface is too small.
+ */
+ if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base))
+ static_key_slow_dec(&supports_deactivate);
+
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);
@@ -1038,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,
@@ -1054,7 +1264,7 @@ gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
processor = (struct acpi_madt_generic_interrupt *)header;
- if (BAD_MADT_ENTRY(processor, end))
+ if (BAD_MADT_GICC_ENTRY(processor, end))
return -EINVAL;
/*
@@ -1070,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);
@@ -1131,14 +1338,33 @@ gic_v2_acpi_init(struct acpi_table_header *table)
}
/*
- * 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()).
+ * Disable split EOI/Deactivate if HYP is not available. ACPI
+ * guarantees that we'll always have a GICv2, so the CPU
+ * interface will always be the right size.
+ */
+ if (!is_hyp_mode_available())
+ static_key_slow_dec(&supports_deactivate);
+
+ /*
+ * 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/kernel/drivers/irqchip/irq-hip04.c b/kernel/drivers/irqchip/irq-hip04.c
index 7d6ffb5de..9688d2e2a 100644
--- a/kernel/drivers/irqchip/irq-hip04.c
+++ b/kernel/drivers/irqchip/irq-hip04.c
@@ -41,6 +41,7 @@
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic.h>
#include <asm/irq.h>
@@ -48,7 +49,6 @@
#include <asm/smp_plat.h>
#include "irq-gic-common.h"
-#include "irqchip.h"
#define HIP04_MAX_IRQS 510
@@ -202,6 +202,9 @@ static struct irq_chip hip04_irq_chip = {
#ifdef CONFIG_SMP
.irq_set_affinity = hip04_irq_set_affinity,
#endif
+ .flags = IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE |
+ IRQCHIP_MASK_ON_SUSPEND,
};
static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
@@ -304,11 +307,11 @@ static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_percpu_devid_irq);
- set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &hip04_irq_chip,
handle_fasteoi_irq);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
}
irq_set_chip_data(irq, d->host_data);
return 0;
@@ -322,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/kernel/drivers/irqchip/irq-i8259.c b/kernel/drivers/irqchip/irq-i8259.c
new file mode 100644
index 000000000..6b304eb39
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-i8259.c
@@ -0,0 +1,384 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Code to handle x86 style IRQs plus some generic interrupt stuff.
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Copyright (C) 1994 - 2000 Ralf Baechle
+ */
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
+#include <linux/irq.h>
+
+#include <asm/i8259.h>
+#include <asm/io.h>
+
+/*
+ * This is the 'legacy' 8259A Programmable Interrupt Controller,
+ * present in the majority of PC/AT boxes.
+ * plus some generic x86 specific things if generic specifics makes
+ * any sense at all.
+ * this file should become arch/i386/kernel/irq.c when the old irq.c
+ * moves to arch independent land
+ */
+
+static int i8259A_auto_eoi = -1;
+DEFINE_RAW_SPINLOCK(i8259A_lock);
+static void disable_8259A_irq(struct irq_data *d);
+static void enable_8259A_irq(struct irq_data *d);
+static void mask_and_ack_8259A(struct irq_data *d);
+static void init_8259A(int auto_eoi);
+
+static struct irq_chip i8259A_chip = {
+ .name = "XT-PIC",
+ .irq_mask = disable_8259A_irq,
+ .irq_disable = disable_8259A_irq,
+ .irq_unmask = enable_8259A_irq,
+ .irq_mask_ack = mask_and_ack_8259A,
+};
+
+/*
+ * 8259A PIC functions to handle ISA devices:
+ */
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ */
+static unsigned int cached_irq_mask = 0xffff;
+
+#define cached_master_mask (cached_irq_mask)
+#define cached_slave_mask (cached_irq_mask >> 8)
+
+static void disable_8259A_irq(struct irq_data *d)
+{
+ unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ mask = 1 << irq;
+ raw_spin_lock_irqsave(&i8259A_lock, flags);
+ cached_irq_mask |= mask;
+ if (irq & 8)
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
+ else
+ outb(cached_master_mask, PIC_MASTER_IMR);
+ raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+}
+
+static void enable_8259A_irq(struct irq_data *d)
+{
+ unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ mask = ~(1 << irq);
+ raw_spin_lock_irqsave(&i8259A_lock, flags);
+ cached_irq_mask &= mask;
+ if (irq & 8)
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
+ else
+ outb(cached_master_mask, PIC_MASTER_IMR);
+ raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+}
+
+int i8259A_irq_pending(unsigned int irq)
+{
+ unsigned int mask;
+ unsigned long flags;
+ int ret;
+
+ irq -= I8259A_IRQ_BASE;
+ mask = 1 << irq;
+ raw_spin_lock_irqsave(&i8259A_lock, flags);
+ if (irq < 8)
+ ret = inb(PIC_MASTER_CMD) & mask;
+ else
+ ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
+ raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+
+ return ret;
+}
+
+void make_8259A_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_set_chip_and_handler(irq, &i8259A_chip, handle_level_irq);
+ enable_irq(irq);
+}
+
+/*
+ * This function assumes to be called rarely. Switching between
+ * 8259A registers is slow.
+ * This has to be protected by the irq controller spinlock
+ * before being called.
+ */
+static inline int i8259A_irq_real(unsigned int irq)
+{
+ int value;
+ int irqmask = 1 << irq;
+
+ if (irq < 8) {
+ outb(0x0B, PIC_MASTER_CMD); /* ISR register */
+ value = inb(PIC_MASTER_CMD) & irqmask;
+ outb(0x0A, PIC_MASTER_CMD); /* back to the IRR register */
+ return value;
+ }
+ outb(0x0B, PIC_SLAVE_CMD); /* ISR register */
+ value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
+ outb(0x0A, PIC_SLAVE_CMD); /* back to the IRR register */
+ return value;
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+static void mask_and_ack_8259A(struct irq_data *d)
+{
+ unsigned int irqmask, irq = d->irq - I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ irqmask = 1 << irq;
+ raw_spin_lock_irqsave(&i8259A_lock, flags);
+ /*
+ * Lightweight spurious IRQ detection. We do not want
+ * to overdo spurious IRQ handling - it's usually a sign
+ * of hardware problems, so we only do the checks we can
+ * do without slowing down good hardware unnecessarily.
+ *
+ * Note that IRQ7 and IRQ15 (the two spurious IRQs
+ * usually resulting from the 8259A-1|2 PICs) occur
+ * even if the IRQ is masked in the 8259A. Thus we
+ * can check spurious 8259A IRQs without doing the
+ * quite slow i8259A_irq_real() call for every IRQ.
+ * This does not cover 100% of spurious interrupts,
+ * but should be enough to warn the user that there
+ * is something bad going on ...
+ */
+ if (cached_irq_mask & irqmask)
+ goto spurious_8259A_irq;
+ cached_irq_mask |= irqmask;
+
+handle_real_irq:
+ if (irq & 8) {
+ inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
+ outb(0x60+(irq&7), PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
+ outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
+ } else {
+ inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
+ outb(cached_master_mask, PIC_MASTER_IMR);
+ outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
+ }
+ raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+ return;
+
+spurious_8259A_irq:
+ /*
+ * this is the slow path - should happen rarely.
+ */
+ if (i8259A_irq_real(irq))
+ /*
+ * oops, the IRQ _is_ in service according to the
+ * 8259A - not spurious, go handle it.
+ */
+ goto handle_real_irq;
+
+ {
+ static int spurious_irq_mask;
+ /*
+ * At this point we can be sure the IRQ is spurious,
+ * lets ACK and report it. [once per IRQ]
+ */
+ if (!(spurious_irq_mask & irqmask)) {
+ printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
+ spurious_irq_mask |= irqmask;
+ }
+ atomic_inc(&irq_err_count);
+ /*
+ * Theoretically we do not have to handle this IRQ,
+ * but in Linux this does not cause problems and is
+ * simpler for us.
+ */
+ goto handle_real_irq;
+ }
+}
+
+static void i8259A_resume(void)
+{
+ if (i8259A_auto_eoi >= 0)
+ init_8259A(i8259A_auto_eoi);
+}
+
+static void i8259A_shutdown(void)
+{
+ /* Put the i8259A into a quiescent state that
+ * the kernel initialization code can get it
+ * out of.
+ */
+ if (i8259A_auto_eoi >= 0) {
+ outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
+ outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
+ }
+}
+
+static struct syscore_ops i8259_syscore_ops = {
+ .resume = i8259A_resume,
+ .shutdown = i8259A_shutdown,
+};
+
+static int __init i8259A_init_sysfs(void)
+{
+ register_syscore_ops(&i8259_syscore_ops);
+ return 0;
+}
+
+device_initcall(i8259A_init_sysfs);
+
+static void init_8259A(int auto_eoi)
+{
+ unsigned long flags;
+
+ i8259A_auto_eoi = auto_eoi;
+
+ raw_spin_lock_irqsave(&i8259A_lock, flags);
+
+ outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
+ outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
+
+ /*
+ * outb_p - this has to work on a wide range of PC hardware.
+ */
+ outb_p(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
+ outb_p(I8259A_IRQ_BASE + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0 mapped to I8259A_IRQ_BASE + 0x00 */
+ outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */
+ if (auto_eoi) /* master does Auto EOI */
+ outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
+ else /* master expects normal EOI */
+ outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
+
+ outb_p(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
+ outb_p(I8259A_IRQ_BASE + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0 mapped to I8259A_IRQ_BASE + 0x08 */
+ outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
+ outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
+ if (auto_eoi)
+ /*
+ * In AEOI mode we just have to mask the interrupt
+ * when acking.
+ */
+ i8259A_chip.irq_mask_ack = disable_8259A_irq;
+ else
+ i8259A_chip.irq_mask_ack = mask_and_ack_8259A;
+
+ udelay(100); /* wait for 8259A to initialize */
+
+ outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
+ outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
+
+ raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+}
+
+/*
+ * IRQ2 is cascade interrupt to second interrupt controller
+ */
+static struct irqaction irq2 = {
+ .handler = no_action,
+ .name = "cascade",
+ .flags = IRQF_NO_THREAD,
+};
+
+static struct resource pic1_io_resource = {
+ .name = "pic1",
+ .start = PIC_MASTER_CMD,
+ .end = PIC_MASTER_IMR,
+ .flags = IORESOURCE_BUSY
+};
+
+static struct resource pic2_io_resource = {
+ .name = "pic2",
+ .start = PIC_SLAVE_CMD,
+ .end = PIC_SLAVE_IMR,
+ .flags = IORESOURCE_BUSY
+};
+
+static int i8259A_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &i8259A_chip, handle_level_irq);
+ irq_set_probe(virq);
+ return 0;
+}
+
+static struct irq_domain_ops i8259A_ops = {
+ .map = i8259A_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+/*
+ * On systems with i8259-style interrupt controllers we assume for
+ * driver compatibility reasons interrupts 0 - 15 to be the i8259
+ * interrupts even if the hardware uses a different interrupt numbering.
+ */
+struct irq_domain * __init __init_i8259_irqs(struct device_node *node)
+{
+ struct irq_domain *domain;
+
+ insert_resource(&ioport_resource, &pic1_io_resource);
+ insert_resource(&ioport_resource, &pic2_io_resource);
+
+ init_8259A(0);
+
+ domain = irq_domain_add_legacy(node, 16, I8259A_IRQ_BASE, 0,
+ &i8259A_ops, NULL);
+ if (!domain)
+ panic("Failed to add i8259 IRQ domain");
+
+ setup_irq(I8259A_IRQ_BASE + PIC_CASCADE_IR, &irq2);
+ return domain;
+}
+
+void __init init_i8259_irqs(void)
+{
+ __init_i8259_irqs(NULL);
+}
+
+static void i8259_irq_dispatch(struct irq_desc *desc)
+{
+ struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ int hwirq = i8259_irq();
+ unsigned int irq;
+
+ if (hwirq < 0)
+ return;
+
+ irq = irq_linear_revmap(domain, hwirq);
+ generic_handle_irq(irq);
+}
+
+int __init i8259_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct irq_domain *domain;
+ unsigned int parent_irq;
+
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (!parent_irq) {
+ pr_err("Failed to map i8259 parent IRQ\n");
+ return -ENODEV;
+ }
+
+ domain = __init_i8259_irqs(node);
+ 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/kernel/drivers/irqchip/irq-imgpdc.c b/kernel/drivers/irqchip/irq-imgpdc.c
index 8071c2eb0..c02d29c9d 100644
--- a/kernel/drivers/irqchip/irq-imgpdc.c
+++ b/kernel/drivers/irqchip/irq-imgpdc.c
@@ -218,8 +218,9 @@ static int pdc_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
-static void pdc_intc_perip_isr(unsigned int irq, struct irq_desc *desc)
+static void pdc_intc_perip_isr(struct irq_desc *desc)
{
+ unsigned int irq = irq_desc_get_irq(desc);
struct pdc_intc_priv *priv;
unsigned int i, irq_no;
@@ -239,7 +240,7 @@ found:
generic_handle_irq(irq_no);
}
-static void pdc_intc_syswake_isr(unsigned int irq, struct irq_desc *desc)
+static void pdc_intc_syswake_isr(struct irq_desc *desc)
{
struct pdc_intc_priv *priv;
unsigned int syswake, irq_no;
@@ -451,13 +452,13 @@ static int pdc_intc_probe(struct platform_device *pdev)
/* Setup chained handlers for the peripheral IRQs */
for (i = 0; i < priv->nr_perips; ++i) {
irq = priv->perip_irqs[i];
- irq_set_handler_data(irq, priv);
- irq_set_chained_handler(irq, pdc_intc_perip_isr);
+ irq_set_chained_handler_and_data(irq, pdc_intc_perip_isr,
+ priv);
}
/* Setup chained handler for the syswake IRQ */
- irq_set_handler_data(priv->syswake_irq, priv);
- irq_set_chained_handler(priv->syswake_irq, pdc_intc_syswake_isr);
+ irq_set_chained_handler_and_data(priv->syswake_irq,
+ pdc_intc_syswake_isr, priv);
dev_info(&pdev->dev,
"PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n",
diff --git a/kernel/drivers/irqchip/irq-imx-gpcv2.c b/kernel/drivers/irqchip/irq-imx-gpcv2.c
new file mode 100644
index 000000000..15af9a975
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-imx-gpcv2.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * 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/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/irqchip.h>
+#include <linux/syscore_ops.h>
+
+#define IMR_NUM 4
+#define GPC_MAX_IRQS (IMR_NUM * 32)
+
+#define GPC_IMR1_CORE0 0x30
+#define GPC_IMR1_CORE1 0x40
+
+struct gpcv2_irqchip_data {
+ struct raw_spinlock rlock;
+ void __iomem *gpc_base;
+ u32 wakeup_sources[IMR_NUM];
+ u32 saved_irq_mask[IMR_NUM];
+ u32 cpu2wakeup;
+};
+
+static struct gpcv2_irqchip_data *imx_gpcv2_instance;
+
+/*
+ * Interface for the low level wakeup code.
+ */
+u32 imx_gpcv2_get_wakeup_source(u32 **sources)
+{
+ if (!imx_gpcv2_instance)
+ return 0;
+
+ if (sources)
+ *sources = imx_gpcv2_instance->wakeup_sources;
+
+ return IMR_NUM;
+}
+
+static int gpcv2_wakeup_source_save(void)
+{
+ struct gpcv2_irqchip_data *cd;
+ void __iomem *reg;
+ int i;
+
+ cd = imx_gpcv2_instance;
+ if (!cd)
+ return 0;
+
+ for (i = 0; i < IMR_NUM; i++) {
+ reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
+ cd->saved_irq_mask[i] = readl_relaxed(reg);
+ writel_relaxed(cd->wakeup_sources[i], reg);
+ }
+
+ return 0;
+}
+
+static void gpcv2_wakeup_source_restore(void)
+{
+ struct gpcv2_irqchip_data *cd;
+ void __iomem *reg;
+ int i;
+
+ cd = imx_gpcv2_instance;
+ if (!cd)
+ return;
+
+ for (i = 0; i < IMR_NUM; i++) {
+ reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
+ writel_relaxed(cd->saved_irq_mask[i], reg);
+ }
+}
+
+static struct syscore_ops imx_gpcv2_syscore_ops = {
+ .suspend = gpcv2_wakeup_source_save,
+ .resume = gpcv2_wakeup_source_restore,
+};
+
+static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct gpcv2_irqchip_data *cd = d->chip_data;
+ unsigned int idx = d->hwirq / 32;
+ unsigned long flags;
+ void __iomem *reg;
+ u32 mask, val;
+
+ raw_spin_lock_irqsave(&cd->rlock, flags);
+ reg = cd->gpc_base + cd->cpu2wakeup + idx * 4;
+ mask = 1 << d->hwirq % 32;
+ val = cd->wakeup_sources[idx];
+
+ cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask);
+ raw_spin_unlock_irqrestore(&cd->rlock, flags);
+
+ /*
+ * Do *not* call into the parent, as the GIC doesn't have any
+ * wake-up facility...
+ */
+
+ return 0;
+}
+
+static void imx_gpcv2_irq_unmask(struct irq_data *d)
+{
+ struct gpcv2_irqchip_data *cd = d->chip_data;
+ void __iomem *reg;
+ u32 val;
+
+ raw_spin_lock(&cd->rlock);
+ reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
+ val = readl_relaxed(reg);
+ val &= ~(1 << d->hwirq % 32);
+ writel_relaxed(val, reg);
+ raw_spin_unlock(&cd->rlock);
+
+ irq_chip_unmask_parent(d);
+}
+
+static void imx_gpcv2_irq_mask(struct irq_data *d)
+{
+ struct gpcv2_irqchip_data *cd = d->chip_data;
+ void __iomem *reg;
+ u32 val;
+
+ raw_spin_lock(&cd->rlock);
+ reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
+ val = readl_relaxed(reg);
+ val |= 1 << (d->hwirq % 32);
+ writel_relaxed(val, reg);
+ raw_spin_unlock(&cd->rlock);
+
+ irq_chip_mask_parent(d);
+}
+
+static struct irq_chip gpcv2_irqchip_data_chip = {
+ .name = "GPCv2",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = imx_gpcv2_irq_mask,
+ .irq_unmask = imx_gpcv2_irq_unmask,
+ .irq_set_wake = imx_gpcv2_irq_set_wake,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+#endif
+};
+
+static int imx_gpcv2_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
+
+ /* 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 imx_gpcv2_domain_alloc(struct irq_domain *domain,
+ unsigned int irq, unsigned int nr_irqs,
+ void *data)
+{
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int err;
+ int i;
+
+ err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
+ if (err)
+ return err;
+
+ if (hwirq >= GPC_MAX_IRQS)
+ return -EINVAL;
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
+ &gpcv2_irqchip_data_chip, domain->host_data);
+ }
+
+ 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 = {
+ .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,
+ struct device_node *parent)
+{
+ struct irq_domain *parent_domain, *domain;
+ struct gpcv2_irqchip_data *cd;
+ int i;
+
+ if (!parent) {
+ pr_err("%s: no parent, giving up\n", node->full_name);
+ return -ENODEV;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("%s: unable to get parent domain\n", node->full_name);
+ return -ENXIO;
+ }
+
+ cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL);
+ if (!cd) {
+ pr_err("kzalloc failed!\n");
+ return -ENOMEM;
+ }
+
+ cd->gpc_base = of_iomap(node, 0);
+ if (!cd->gpc_base) {
+ pr_err("fsl-gpcv2: unable to map gpc registers\n");
+ kfree(cd);
+ return -ENOMEM;
+ }
+
+ domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
+ node, &gpcv2_irqchip_data_domain_ops, cd);
+ if (!domain) {
+ iounmap(cd->gpc_base);
+ kfree(cd);
+ return -ENOMEM;
+ }
+ irq_set_default_host(domain);
+
+ /* Initially mask all interrupts */
+ for (i = 0; i < IMR_NUM; i++) {
+ writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE0 + i * 4);
+ writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE1 + i * 4);
+ cd->wakeup_sources[i] = ~0;
+ }
+
+ /* Let CORE0 as the default CPU to wake up by GPC */
+ cd->cpu2wakeup = GPC_IMR1_CORE0;
+
+ /*
+ * Due to hardware design failure, need to make sure GPR
+ * interrupt(#32) is unmasked during RUN mode to avoid entering
+ * DSM by mistake.
+ */
+ writel_relaxed(~0x1, cd->gpc_base + cd->cpu2wakeup);
+
+ imx_gpcv2_instance = cd;
+ register_syscore_ops(&imx_gpcv2_syscore_ops);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init);
diff --git a/kernel/drivers/irqchip/irq-ingenic.c b/kernel/drivers/irqchip/irq-ingenic.c
new file mode 100644
index 000000000..fc5953dea
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-ingenic.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 platform IRQ 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 the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/ingenic.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/mach-jz4740/irq.h>
+
+struct ingenic_intc_data {
+ void __iomem *base;
+ unsigned num_chips;
+};
+
+#define JZ_REG_INTC_STATUS 0x00
+#define JZ_REG_INTC_MASK 0x04
+#define JZ_REG_INTC_SET_MASK 0x08
+#define JZ_REG_INTC_CLEAR_MASK 0x0c
+#define JZ_REG_INTC_PENDING 0x10
+#define CHIP_SIZE 0x20
+
+static irqreturn_t intc_cascade(int irq, void *data)
+{
+ struct ingenic_intc_data *intc = irq_get_handler_data(irq);
+ uint32_t irq_reg;
+ unsigned i;
+
+ for (i = 0; i < intc->num_chips; i++) {
+ irq_reg = readl(intc->base + (i * CHIP_SIZE) +
+ JZ_REG_INTC_PENDING);
+ if (!irq_reg)
+ continue;
+
+ generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask)
+{
+ struct irq_chip_regs *regs = &gc->chip_types->regs;
+
+ writel(mask, gc->reg_base + regs->enable);
+ writel(~mask, gc->reg_base + regs->disable);
+}
+
+void ingenic_intc_irq_suspend(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ intc_irq_set_mask(gc, gc->wake_active);
+}
+
+void ingenic_intc_irq_resume(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ intc_irq_set_mask(gc, gc->mask_cache);
+}
+
+static struct irqaction intc_cascade_action = {
+ .handler = intc_cascade,
+ .name = "SoC intc cascade interrupt",
+};
+
+static int __init ingenic_intc_of_init(struct device_node *node,
+ unsigned num_chips)
+{
+ struct ingenic_intc_data *intc;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ struct irq_domain *domain;
+ int parent_irq, err = 0;
+ unsigned i;
+
+ intc = kzalloc(sizeof(*intc), GFP_KERNEL);
+ if (!intc) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (!parent_irq) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ err = irq_set_handler_data(parent_irq, intc);
+ if (err)
+ goto out_unmap_irq;
+
+ intc->num_chips = num_chips;
+ intc->base = of_iomap(node, 0);
+ if (!intc->base) {
+ err = -ENODEV;
+ goto out_unmap_irq;
+ }
+
+ for (i = 0; i < num_chips; i++) {
+ /* Mask all irqs */
+ writel(0xffffffff, intc->base + (i * CHIP_SIZE) +
+ JZ_REG_INTC_SET_MASK);
+
+ gc = irq_alloc_generic_chip("INTC", 1,
+ JZ4740_IRQ_BASE + (i * 32),
+ intc->base + (i * CHIP_SIZE),
+ handle_level_irq);
+
+ gc->wake_enabled = IRQ_MSK(32);
+
+ ct = gc->chip_types;
+ ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
+ ct->regs.disable = JZ_REG_INTC_SET_MASK;
+ ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
+ ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
+ ct->chip.irq_set_wake = irq_gc_set_wake;
+ ct->chip.irq_suspend = ingenic_intc_irq_suspend;
+ ct->chip.irq_resume = ingenic_intc_irq_resume;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
+ IRQ_NOPROBE | IRQ_LEVEL);
+ }
+
+ domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!domain)
+ pr_warn("unable to register IRQ domain\n");
+
+ setup_irq(parent_irq, &intc_cascade_action);
+ return 0;
+
+out_unmap_irq:
+ irq_dispose_mapping(parent_irq);
+out_free:
+ kfree(intc);
+out_err:
+ return err;
+}
+
+static int __init intc_1chip_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return ingenic_intc_of_init(node, 1);
+}
+IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
+
+static int __init intc_2chip_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return ingenic_intc_of_init(node, 2);
+}
+IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init);
+IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init);
+IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);
diff --git a/kernel/drivers/irqchip/irq-keystone.c b/kernel/drivers/irqchip/irq-keystone.c
index 78e8b3ce5..deb89d63a 100644
--- a/kernel/drivers/irqchip/irq-keystone.c
+++ b/kernel/drivers/irqchip/irq-keystone.c
@@ -20,13 +20,12 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
-#include "irqchip.h"
-
/* The source ID bits start from 4 to 31 (total 28 bits)*/
#define BIT_OFS 4
@@ -84,8 +83,9 @@ static void keystone_irq_ack(struct irq_data *d)
/* nothing to do here */
}
-static void keystone_irq_handler(unsigned irq, struct irq_desc *desc)
+static void keystone_irq_handler(struct irq_desc *desc)
{
+ unsigned int irq = irq_desc_get_irq(desc);
struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
unsigned long pending;
int src, virq;
@@ -127,11 +127,11 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
irq_set_chip_data(virq, kirq);
irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
- set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(virq);
return 0;
}
-static struct irq_domain_ops keystone_irq_ops = {
+static const struct irq_domain_ops keystone_irq_ops = {
.map = keystone_irq_map,
.xlate = irq_domain_xlate_onecell,
};
@@ -184,8 +184,7 @@ static int keystone_irq_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, kirq);
- irq_set_chained_handler(kirq->irq, keystone_irq_handler);
- irq_set_handler_data(kirq->irq, kirq);
+ irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
/* clear all source bits */
keystone_irq_writel(kirq, ~0x0);
diff --git a/kernel/drivers/irqchip/irq-metag-ext.c b/kernel/drivers/irqchip/irq-metag-ext.c
index 2cb474ad8..8c38b3d92 100644
--- a/kernel/drivers/irqchip/irq-metag-ext.c
+++ b/kernel/drivers/irqchip/irq-metag-ext.c
@@ -404,7 +404,6 @@ static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type)
#ifdef CONFIG_METAG_SUSPEND_MEM
struct meta_intc_priv *priv = &meta_intc_priv;
#endif
- unsigned int irq = data->irq;
irq_hw_number_t hw = data->hwirq;
unsigned int bit = 1 << meta_intc_offset(hw);
void __iomem *level_addr = meta_intc_level_addr(hw);
@@ -413,11 +412,11 @@ static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type)
/* update the chip/handler */
if (flow_type & IRQ_TYPE_LEVEL_MASK)
- __irq_set_chip_handler_name_locked(irq, &meta_intc_level_chip,
- handle_level_irq, NULL);
+ irq_set_chip_handler_name_locked(data, &meta_intc_level_chip,
+ handle_level_irq, NULL);
else
- __irq_set_chip_handler_name_locked(irq, &meta_intc_edge_chip,
- handle_edge_irq, NULL);
+ irq_set_chip_handler_name_locked(data, &meta_intc_edge_chip,
+ handle_edge_irq, NULL);
/* and clear/set the bit in HWLEVELEXT */
__global_lock2(flags);
@@ -447,7 +446,7 @@ static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type)
* Whilst using TR2 to detect external interrupts is a software convention it is
* (hopefully) unlikely to change.
*/
-static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc)
+static void meta_intc_irq_demux(struct irq_desc *desc)
{
struct meta_intc_priv *priv = &meta_intc_priv;
irq_hw_number_t hw;
diff --git a/kernel/drivers/irqchip/irq-metag.c b/kernel/drivers/irqchip/irq-metag.c
index c16c186d9..a5f053bd2 100644
--- a/kernel/drivers/irqchip/irq-metag.c
+++ b/kernel/drivers/irqchip/irq-metag.c
@@ -220,7 +220,7 @@ static int metag_internal_irq_set_affinity(struct irq_data *data,
* occurred. It is this function's job to demux this irq and
* figure out exactly which trigger needs servicing.
*/
-static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc)
+static void metag_internal_irq_demux(struct irq_desc *desc)
{
struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc);
irq_hw_number_t hw;
@@ -286,8 +286,7 @@ static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv,
int irq = tbisig_map(signum);
/* Register the multiplexed IRQ handler */
- irq_set_handler_data(irq, priv);
- irq_set_chained_handler(irq, metag_internal_irq_demux);
+ irq_set_chained_handler_and_data(irq, metag_internal_irq_demux, priv);
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
}
diff --git a/kernel/drivers/irqchip/irq-mips-cpu.c b/kernel/drivers/irqchip/irq-mips-cpu.c
new file mode 100644
index 000000000..8c504f562
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-mips-cpu.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * Copyright (C) 2001 Ralf Baechle
+ * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.
+ * Author: Maciej W. Rozycki <macro@mips.com>
+ *
+ * This file define the irq handler for MIPS CPU interrupts.
+ *
+ * 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.
+ */
+
+/*
+ * Almost all MIPS CPUs define 8 interrupt sources. They are typically
+ * level triggered (i.e., cannot be cleared from CPU; must be cleared from
+ * device). The first two are software interrupts which we don't really
+ * use or support. The last one is usually the CPU timer interrupt if
+ * counter register is present or, for CPUs with an external FPU, by
+ * convention it's the FPU exception interrupt.
+ *
+ * Don't even think about using this on SMP. You have been warned.
+ *
+ * This file exports one global function:
+ * void mips_cpu_irq_init(void);
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/mipsmtregs.h>
+#include <asm/setup.h>
+
+static inline void unmask_mips_irq(struct irq_data *d)
+{
+ set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ irq_enable_hazard();
+}
+
+static inline void mask_mips_irq(struct irq_data *d)
+{
+ clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ irq_disable_hazard();
+}
+
+static struct irq_chip mips_cpu_irq_controller = {
+ .name = "MIPS",
+ .irq_ack = mask_mips_irq,
+ .irq_mask = mask_mips_irq,
+ .irq_mask_ack = mask_mips_irq,
+ .irq_unmask = unmask_mips_irq,
+ .irq_eoi = unmask_mips_irq,
+ .irq_disable = mask_mips_irq,
+ .irq_enable = unmask_mips_irq,
+};
+
+/*
+ * Basically the same as above but taking care of all the MT stuff
+ */
+
+static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
+{
+ unsigned int vpflags = dvpe();
+
+ clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ evpe(vpflags);
+ unmask_mips_irq(d);
+ return 0;
+}
+
+/*
+ * While we ack the interrupt interrupts are disabled and thus we don't need
+ * to deal with concurrency issues. Same for mips_cpu_irq_end.
+ */
+static void mips_mt_cpu_irq_ack(struct irq_data *d)
+{
+ unsigned int vpflags = dvpe();
+ clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
+ evpe(vpflags);
+ mask_mips_irq(d);
+}
+
+static struct irq_chip mips_mt_cpu_irq_controller = {
+ .name = "MIPS",
+ .irq_startup = mips_mt_cpu_irq_startup,
+ .irq_ack = mips_mt_cpu_irq_ack,
+ .irq_mask = mask_mips_irq,
+ .irq_mask_ack = mips_mt_cpu_irq_ack,
+ .irq_unmask = unmask_mips_irq,
+ .irq_eoi = unmask_mips_irq,
+ .irq_disable = mask_mips_irq,
+ .irq_enable = unmask_mips_irq,
+};
+
+asmlinkage void __weak plat_irq_dispatch(void)
+{
+ unsigned long pending = read_c0_cause() & read_c0_status() & ST0_IM;
+ int irq;
+
+ if (!pending) {
+ spurious_interrupt();
+ return;
+ }
+
+ pending >>= CAUSEB_IP;
+ while (pending) {
+ irq = fls(pending) - 1;
+ do_IRQ(MIPS_CPU_IRQ_BASE + irq);
+ pending &= ~BIT(irq);
+ }
+}
+
+static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ static struct irq_chip *chip;
+
+ if (hw < 2 && cpu_has_mipsmt) {
+ /* Software interrupts are used for MT/CMT IPI */
+ chip = &mips_mt_cpu_irq_controller;
+ } else {
+ chip = &mips_cpu_irq_controller;
+ }
+
+ if (cpu_has_vint)
+ set_vi_handler(hw, plat_irq_dispatch);
+
+ irq_set_chip_and_handler(irq, chip, handle_percpu_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
+ .map = mips_cpu_intc_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void __init __mips_cpu_irq_init(struct device_node *of_node)
+{
+ struct irq_domain *domain;
+
+ /* Mask interrupts. */
+ clear_c0_status(ST0_IM);
+ clear_c0_cause(CAUSEF_IP);
+
+ domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
+ &mips_cpu_intc_irq_domain_ops, NULL);
+ if (!domain)
+ panic("Failed to add irqdomain for MIPS CPU");
+}
+
+void __init mips_cpu_irq_init(void)
+{
+ __mips_cpu_irq_init(NULL);
+}
+
+int __init mips_cpu_irq_of_init(struct device_node *of_node,
+ struct device_node *parent)
+{
+ __mips_cpu_irq_init(of_node);
+ return 0;
+}
+IRQCHIP_DECLARE(cpu_intc, "mti,cpu-interrupt-controller", mips_cpu_irq_of_init);
diff --git a/kernel/drivers/irqchip/irq-mips-gic.c b/kernel/drivers/irqchip/irq-mips-gic.c
index 269c2354c..9e17ef27a 100644
--- a/kernel/drivers/irqchip/irq-mips-gic.c
+++ b/kernel/drivers/irqchip/irq-mips-gic.c
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/mips-gic.h>
#include <linux/of_address.h>
#include <linux/sched.h>
@@ -22,14 +23,13 @@
#include <dt-bindings/interrupt-controller/mips-gic.h>
-#include "irqchip.h"
-
unsigned int gic_present;
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);
@@ -42,20 +42,46 @@ static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
static void __gic_irq_dispatch(void);
-static inline unsigned int gic_read(unsigned int reg)
+static inline u32 gic_read32(unsigned int reg)
{
return __raw_readl(gic_base + reg);
}
-static inline void gic_write(unsigned int reg, unsigned int val)
+static inline u64 gic_read64(unsigned int reg)
+{
+ return __raw_readq(gic_base + reg);
+}
+
+static inline unsigned long gic_read(unsigned int reg)
+{
+ if (!mips_cm_is64)
+ return gic_read32(reg);
+ else
+ return gic_read64(reg);
+}
+
+static inline void gic_write32(unsigned int reg, u32 val)
+{
+ return __raw_writel(val, gic_base + reg);
+}
+
+static inline void gic_write64(unsigned int reg, u64 val)
{
- __raw_writel(val, gic_base + reg);
+ return __raw_writeq(val, gic_base + reg);
+}
+
+static inline void gic_write(unsigned int reg, unsigned long val)
+{
+ if (!mips_cm_is64)
+ return gic_write32(reg, (u32)val);
+ else
+ return gic_write64(reg, (u64)val);
}
-static inline void gic_update_bits(unsigned int reg, unsigned int mask,
- unsigned int val)
+static inline void gic_update_bits(unsigned int reg, unsigned long mask,
+ unsigned long val)
{
- unsigned int regval;
+ unsigned long regval;
regval = gic_read(reg);
regval &= ~mask;
@@ -66,40 +92,40 @@ static inline void gic_update_bits(unsigned int reg, unsigned int mask,
static inline void gic_reset_mask(unsigned int intr)
{
gic_write(GIC_REG(SHARED, GIC_SH_RMASK) + GIC_INTR_OFS(intr),
- 1 << GIC_INTR_BIT(intr));
+ 1ul << GIC_INTR_BIT(intr));
}
static inline void gic_set_mask(unsigned int intr)
{
gic_write(GIC_REG(SHARED, GIC_SH_SMASK) + GIC_INTR_OFS(intr),
- 1 << GIC_INTR_BIT(intr));
+ 1ul << GIC_INTR_BIT(intr));
}
static inline void gic_set_polarity(unsigned int intr, unsigned int pol)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_POLARITY) +
- GIC_INTR_OFS(intr), 1 << GIC_INTR_BIT(intr),
- pol << GIC_INTR_BIT(intr));
+ GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),
+ (unsigned long)pol << GIC_INTR_BIT(intr));
}
static inline void gic_set_trigger(unsigned int intr, unsigned int trig)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_TRIGGER) +
- GIC_INTR_OFS(intr), 1 << GIC_INTR_BIT(intr),
- trig << GIC_INTR_BIT(intr));
+ GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),
+ (unsigned long)trig << GIC_INTR_BIT(intr));
}
static inline void gic_set_dual_edge(unsigned int intr, unsigned int dual)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_DUAL) + GIC_INTR_OFS(intr),
- 1 << GIC_INTR_BIT(intr),
- dual << GIC_INTR_BIT(intr));
+ 1ul << GIC_INTR_BIT(intr),
+ (unsigned long)dual << GIC_INTR_BIT(intr));
}
static inline void gic_map_to_pin(unsigned int intr, unsigned int pin)
{
- gic_write(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_PIN_BASE) +
- GIC_SH_MAP_TO_PIN(intr), GIC_MAP_TO_PIN_MSK | pin);
+ gic_write32(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_PIN_BASE) +
+ GIC_SH_MAP_TO_PIN(intr), GIC_MAP_TO_PIN_MSK | pin);
}
static inline void gic_map_to_vpe(unsigned int intr, unsigned int vpe)
@@ -114,10 +140,13 @@ cycle_t gic_read_count(void)
{
unsigned int hi, hi2, lo;
+ if (mips_cm_is64)
+ return (cycle_t)gic_read(GIC_REG(SHARED, GIC_SH_COUNTER));
+
do {
- hi = gic_read(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
- lo = gic_read(GIC_REG(SHARED, GIC_SH_COUNTER_31_00));
- hi2 = gic_read(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
+ hi = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
+ lo = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_31_00));
+ hi2 = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
} while (hi2 != hi);
return (((cycle_t) hi) << 32) + lo;
@@ -136,10 +165,14 @@ unsigned int gic_get_count_width(void)
void gic_write_compare(cycle_t cnt)
{
- gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI),
- (int)(cnt >> 32));
- gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO),
- (int)(cnt & 0xffffffff));
+ if (mips_cm_is64) {
+ gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE), cnt);
+ } else {
+ gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI),
+ (int)(cnt >> 32));
+ gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO),
+ (int)(cnt & 0xffffffff));
+ }
}
void gic_write_cpu_compare(cycle_t cnt, int cpu)
@@ -149,10 +182,15 @@ void gic_write_cpu_compare(cycle_t cnt, int cpu)
local_irq_save(flags);
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), cpu);
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI),
- (int)(cnt >> 32));
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO),
- (int)(cnt & 0xffffffff));
+
+ if (mips_cm_is64) {
+ gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE), cnt);
+ } else {
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI),
+ (int)(cnt >> 32));
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO),
+ (int)(cnt & 0xffffffff));
+ }
local_irq_restore(flags);
}
@@ -161,8 +199,11 @@ cycle_t gic_read_compare(void)
{
unsigned int hi, lo;
- hi = gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI));
- lo = gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO));
+ if (mips_cm_is64)
+ return (cycle_t)gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE));
+
+ hi = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI));
+ lo = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO));
return (((cycle_t) hi) << 32) + lo;
}
@@ -197,7 +238,7 @@ static bool gic_local_irq_is_routable(int intr)
if (cpu_has_veic)
return true;
- vpe_ctl = gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_CTL));
+ vpe_ctl = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_CTL));
switch (intr) {
case GIC_LOCAL_INT_TIMER:
return vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK;
@@ -257,23 +298,24 @@ int gic_get_c0_fdc_int(void)
return MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
}
- /*
- * Some cores claim the FDC is routable but it doesn't actually seem to
- * be connected.
- */
- switch (current_cpu_type()) {
- case CPU_INTERAPTIV:
- case CPU_PROAPTIV:
- return -1;
- }
-
return irq_create_mapping(gic_irq_domain,
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;
+ unsigned int i, intr, virq, gic_reg_step = mips_cm_is64 ? 8 : 4;
unsigned long *pcpu_mask;
unsigned long pending_reg, intrmask_reg;
DECLARE_BITMAP(pending, GIC_MAX_INTRS);
@@ -288,8 +330,16 @@ static void gic_handle_shared_int(bool chained)
for (i = 0; i < BITS_TO_LONGS(gic_shared_intrs); i++) {
pending[i] = gic_read(pending_reg);
intrmask[i] = gic_read(intrmask_reg);
- pending_reg += 0x4;
- intrmask_reg += 0x4;
+ pending_reg += gic_reg_step;
+ intrmask_reg += gic_reg_step;
+
+ if (!config_enabled(CONFIG_64BIT) || mips_cm_is64)
+ continue;
+
+ pending[i] |= (u64)gic_read(pending_reg) << 32;
+ intrmask[i] |= (u64)gic_read(intrmask_reg) << 32;
+ pending_reg += gic_reg_step;
+ intrmask_reg += gic_reg_step;
}
bitmap_and(pending, pending, intrmask, gic_shared_intrs);
@@ -368,15 +418,12 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
break;
}
- if (is_edge) {
- __irq_set_chip_handler_name_locked(d->irq,
- &gic_edge_irq_controller,
- handle_edge_irq, NULL);
- } else {
- __irq_set_chip_handler_name_locked(d->irq,
- &gic_level_irq_controller,
- handle_level_irq, NULL);
- }
+ if (is_edge)
+ irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller,
+ handle_edge_irq, NULL);
+ else
+ irq_set_chip_handler_name_locked(d, &gic_level_irq_controller,
+ handle_level_irq, NULL);
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
@@ -399,14 +446,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
spin_lock_irqsave(&gic_lock, flags);
/* Re-route this IRQ */
- gic_map_to_vpe(irq, cpumask_first(&tmp));
+ gic_map_to_vpe(irq, mips_cm_vp_id(cpumask_first(&tmp)));
/* Update the pcpu_masks */
for (i = 0; i < NR_CPUS; i++)
clear_bit(irq, pcpu_masks[i].pcpu_mask);
set_bit(irq, pcpu_masks[cpumask_first(&tmp)].pcpu_mask);
- cpumask_copy(d->affinity, cpumask);
+ cpumask_copy(irq_data_get_affinity_mask(d), cpumask);
spin_unlock_irqrestore(&gic_lock, flags);
return IRQ_SET_MASK_OK_NOCOPY;
@@ -439,8 +486,8 @@ static void gic_handle_local_int(bool chained)
unsigned long pending, masked;
unsigned int intr, virq;
- pending = gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_PEND));
- masked = gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_MASK));
+ pending = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_PEND));
+ masked = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_MASK));
bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS);
@@ -463,14 +510,14 @@ static void gic_mask_local_irq(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
- gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_RMASK), 1 << intr);
+ gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_RMASK), 1 << intr);
}
static void gic_unmask_local_irq(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
- gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), 1 << intr);
+ gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), 1 << intr);
}
static struct irq_chip gic_local_irq_controller = {
@@ -488,7 +535,7 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d)
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i);
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << intr);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << intr);
}
spin_unlock_irqrestore(&gic_lock, flags);
}
@@ -502,7 +549,7 @@ static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i);
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), 1 << intr);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), 1 << intr);
}
spin_unlock_irqrestore(&gic_lock, flags);
}
@@ -519,7 +566,7 @@ static void __gic_irq_dispatch(void)
gic_handle_shared_int(false);
}
-static void gic_irq_dispatch(unsigned int irq, struct irq_desc *desc)
+static void gic_irq_dispatch(struct irq_desc *desc)
{
gic_handle_local_int(true);
gic_handle_shared_int(true);
@@ -548,7 +595,7 @@ static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id)
static irqreturn_t ipi_call_interrupt(int irq, void *dev_id)
{
- smp_call_function_interrupt();
+ generic_smp_call_function_interrupt();
return IRQ_HANDLED;
}
@@ -572,7 +619,7 @@ static __init void gic_ipi_init_one(unsigned int intr, int cpu,
GIC_SHARED_TO_HWIRQ(intr));
int i;
- gic_map_to_vpe(intr, cpu);
+ gic_map_to_vpe(intr, mips_cm_vp_id(cpu));
for (i = 0; i < NR_CPUS; i++)
clear_bit(intr, pcpu_masks[i].pcpu_mask);
set_bit(intr, pcpu_masks[cpu].pcpu_mask);
@@ -622,7 +669,7 @@ static void __init gic_basic_init(void)
for (j = 0; j < GIC_NUM_LOCAL_INTRS; j++) {
if (!gic_local_irq_is_routable(j))
continue;
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << j);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << j);
}
}
}
@@ -667,27 +714,32 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
switch (intr) {
case GIC_LOCAL_INT_WD:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_WD_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_WD_MAP), val);
break;
case GIC_LOCAL_INT_COMPARE:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP),
+ val);
break;
case GIC_LOCAL_INT_TIMER:
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_TO_PIN_MSK | timer_cpu_pin;
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP),
+ val);
break;
case GIC_LOCAL_INT_PERFCTR:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP),
+ val);
break;
case GIC_LOCAL_INT_SWINT0:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_SWINT0_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SWINT0_MAP),
+ val);
break;
case GIC_LOCAL_INT_SWINT1:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_SWINT1_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SWINT1_MAP),
+ val);
break;
case GIC_LOCAL_INT_FDC:
- gic_write(GIC_REG(VPE_OTHER, GIC_VPE_FDC_MAP), val);
+ gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_FDC_MAP), val);
break;
default:
pr_err("Invalid local IRQ %d\n", intr);
@@ -746,7 +798,7 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
return 0;
}
-static struct irq_domain_ops gic_irq_domain_ops = {
+static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.xlate = gic_irq_domain_xlate,
};
@@ -758,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));
@@ -792,7 +846,7 @@ static void __init __gic_init(unsigned long gic_base_addr,
*/
if (IS_ENABLED(CONFIG_MIPS_CMP) &&
gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) {
- timer_cpu_pin = gic_read(GIC_REG(VPE_LOCAL,
+ timer_cpu_pin = gic_read32(GIC_REG(VPE_LOCAL,
GIC_VPE_TIMER_MAP)) &
GIC_MAP_MSK;
irq_set_chained_handler(MIPS_CPU_IRQ_BASE +
diff --git a/kernel/drivers/irqchip/irq-mmp.c b/kernel/drivers/irqchip/irq-mmp.c
index c0da57bdb..013fc9659 100644
--- a/kernel/drivers/irqchip/irq-mmp.c
+++ b/kernel/drivers/irqchip/irq-mmp.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/io.h>
#include <linux/ioport.h>
@@ -24,8 +25,6 @@
#include <asm/exception.h>
#include <asm/hardirq.h>
-#include "irqchip.h"
-
#define MAX_ICU_NR 16
#define PJ1_INT_SEL 0x10c
@@ -130,8 +129,9 @@ struct irq_chip icu_irq_chip = {
.irq_unmask = icu_unmask_irq,
};
-static void icu_mux_irq_demux(unsigned int irq, struct irq_desc *desc)
+static void icu_mux_irq_demux(struct irq_desc *desc)
{
+ unsigned int irq = irq_desc_get_irq(desc);
struct irq_domain *domain;
struct icu_chip_data *data;
int i;
@@ -164,7 +164,6 @@ static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
- set_irq_flags(irq, IRQF_VALID);
return 0;
}
@@ -234,7 +233,6 @@ void __init icu_init_irq(void)
for (irq = 0; irq < 64; irq++) {
icu_mask_irq(irq_get_irq_data(irq));
irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
- set_irq_flags(irq, IRQF_VALID);
}
irq_set_default_host(icu_data[0].domain);
set_handle_irq(mmp_handle_irq);
@@ -337,7 +335,6 @@ void __init mmp2_init_icu(void)
irq_set_chip_and_handler(irq, &icu_irq_chip,
handle_level_irq);
}
- set_irq_flags(irq, IRQF_VALID);
}
irq_set_default_host(icu_data[0].domain);
set_handle_irq(mmp2_handle_irq);
diff --git a/kernel/drivers/irqchip/irq-moxart.c b/kernel/drivers/irqchip/irq-moxart.c
index 00b3cc908..a24b06a17 100644
--- a/kernel/drivers/irqchip/irq-moxart.c
+++ b/kernel/drivers/irqchip/irq-moxart.c
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -19,8 +20,6 @@
#include <asm/exception.h>
-#include "irqchip.h"
-
#define IRQ_SOURCE_REG 0
#define IRQ_MASK_REG 0x04
#define IRQ_CLEAR_REG 0x08
diff --git a/kernel/drivers/irqchip/irq-mtk-sysirq.c b/kernel/drivers/irqchip/irq-mtk-sysirq.c
index eaf0a710e..63ac73b1d 100644
--- a/kernel/drivers/irqchip/irq-mtk-sysirq.c
+++ b/kernel/drivers/irqchip/irq-mtk-sysirq.c
@@ -13,6 +13,7 @@
*/
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -21,8 +22,6 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include "irqchip.h"
-
struct mtk_sysirq_chip_data {
spinlock_t lock;
void __iomem *intpol_base;
@@ -68,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,
@@ -91,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 struct irq_domain_ops sysirq_domain_ops = {
- .xlate = mtk_sysirq_domain_xlate,
- .alloc = mtk_sysirq_domain_alloc,
- .free = irq_domain_free_irqs_common,
+static const struct irq_domain_ops sysirq_domain_ops = {
+ .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,
@@ -144,7 +146,7 @@ static int __init mtk_sysirq_of_init(struct device_node *node,
chip_data->intpol_base = ioremap(res.start, size);
if (!chip_data->intpol_base) {
pr_err("mtk_sysirq: unable to map sysirq register\n");
- ret = PTR_ERR(chip_data->intpol_base);
+ ret = -ENXIO;
goto out_free;
}
diff --git a/kernel/drivers/irqchip/irq-mxs.c b/kernel/drivers/irqchip/irq-mxs.c
index e4acf1e3f..efe508459 100644
--- a/kernel/drivers/irqchip/irq-mxs.c
+++ b/kernel/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
@@ -19,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/io.h>
#include <linux/of.h>
@@ -27,22 +30,64 @@
#include <linux/stmp_device.h>
#include <asm/exception.h>
-#include "irqchip.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)
{
/*
@@ -51,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 = {
@@ -72,43 +133,116 @@ 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);
- set_irq_flags(virq, IRQF_VALID);
+ 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;
}
-static struct irq_domain_ops icoll_irq_domain_ops = {
+static const struct irq_domain_ops icoll_irq_domain_ops = {
.map = icoll_irq_domain_map,
.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);
+ set_handle_irq(icoll_handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);
diff --git a/kernel/drivers/irqchip/irq-nvic.c b/kernel/drivers/irqchip/irq-nvic.c
index 4ff0805fc..b1777104f 100644
--- a/kernel/drivers/irqchip/irq-nvic.c
+++ b/kernel/drivers/irqchip/irq-nvic.c
@@ -21,13 +21,12 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <asm/v7m.h>
#include <asm/exception.h>
-#include "irqchip.h"
-
#define NVIC_ISER 0x000
#define NVIC_ICER 0x080
#define NVIC_IPR 0x300
@@ -49,6 +48,41 @@ 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 irq_fwspec *fwspec = arg;
+
+ ret = nvic_irq_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_map_generic_chip(domain, virq + i, hwirq + i);
+
+ return 0;
+}
+
+static const struct irq_domain_ops nvic_irq_domain_ops = {
+ .translate = nvic_irq_domain_translate,
+ .alloc = nvic_irq_domain_alloc,
+ .free = irq_domain_free_irqs_top,
+};
+
static int __init nvic_of_init(struct device_node *node,
struct device_node *parent)
{
@@ -70,7 +104,8 @@ static int __init nvic_of_init(struct device_node *node,
irqs = NVIC_MAX_IRQ;
nvic_irq_domain =
- irq_domain_add_linear(node, irqs, &irq_generic_chip_ops, NULL);
+ irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL);
+
if (!nvic_irq_domain) {
pr_warn("Failed to allocate irq domain\n");
return -ENOMEM;
diff --git a/kernel/drivers/irqchip/irq-omap-intc.c b/kernel/drivers/irqchip/irq-omap-intc.c
index a569c6dbd..f6cb1b8bb 100644
--- a/kernel/drivers/irqchip/irq-omap-intc.c
+++ b/kernel/drivers/irqchip/irq-omap-intc.c
@@ -17,13 +17,12 @@
#include <linux/io.h>
#include <asm/exception.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include "irqchip.h"
-
/* Define these here for now until we drop all board-files */
#define OMAP24XX_IC_BASE 0x480fe000
#define OMAP34XX_IC_BASE 0x48200000
@@ -48,6 +47,7 @@
#define INTC_ILR0 0x0100
#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */
+#define SPURIOUSIRQ_MASK (0x1ffffff << 7)
#define INTCPS_NR_ILR_REGS 128
#define INTCPS_NR_MIR_REGS 4
@@ -331,37 +331,36 @@ static int __init omap_init_irq(u32 base, struct device_node *node)
static asmlinkage void __exception_irq_entry
omap_intc_handle_irq(struct pt_regs *regs)
{
- u32 irqnr = 0;
- int handled_irq = 0;
- int i;
-
- do {
- for (i = 0; i < omap_nr_pending; i++) {
- irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i));
- if (irqnr)
- goto out;
- }
-
-out:
- if (!irqnr)
- break;
+ extern unsigned long irq_err_count;
+ u32 irqnr;
- irqnr = intc_readl(INTC_SIR);
- irqnr &= ACTIVEIRQ_MASK;
-
- if (irqnr) {
- handle_domain_irq(domain, irqnr, regs);
- handled_irq = 1;
- }
- } while (irqnr);
+ irqnr = intc_readl(INTC_SIR);
/*
- * If an irq is masked or deasserted while active, we will
- * keep ending up here with no irq handled. So remove it from
- * the INTC with an ack.
+ * A spurious IRQ can result if interrupt that triggered the
+ * sorting is no longer active during the sorting (10 INTC
+ * functional clock cycles after interrupt assertion). Or a
+ * change in interrupt mask affected the result during sorting
+ * time. There is no special handling required except ignoring
+ * the SIR register value just read and retrying.
+ * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K
+ *
+ * Many a times, a spurious interrupt situation has been fixed
+ * by adding a flush for the posted write acking the IRQ in
+ * the device driver. Typically, this is going be the device
+ * driver whose interrupt was handled just before the spurious
+ * IRQ occurred. Pay attention to those device drivers if you
+ * run into hitting the spurious IRQ condition below.
*/
- if (!handled_irq)
+ if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) {
+ pr_err_once("%s: spurious irq!\n", __func__);
+ irq_err_count++;
omap_ack_irq(NULL);
+ return;
+ }
+
+ irqnr &= ACTIVEIRQ_MASK;
+ handle_domain_irq(domain, irqnr, regs);
}
void __init omap3_init_irq(void)
diff --git a/kernel/drivers/irqchip/irq-or1k-pic.c b/kernel/drivers/irqchip/irq-or1k-pic.c
index e93d079fe..6a9a3e792 100644
--- a/kernel/drivers/irqchip/irq-or1k-pic.c
+++ b/kernel/drivers/irqchip/irq-or1k-pic.c
@@ -9,12 +9,11 @@
*/
#include <linux/irq.h>
+#include <linux/irqchip.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 {
diff --git a/kernel/drivers/irqchip/irq-orion.c b/kernel/drivers/irqchip/irq-orion.c
index ad0c0f6f1..be4c5a8c9 100644
--- a/kernel/drivers/irqchip/irq-orion.c
+++ b/kernel/drivers/irqchip/irq-orion.c
@@ -10,14 +10,13 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
/*
* Orion SoC main interrupt controller
*/
@@ -107,9 +106,9 @@ IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
#define ORION_BRIDGE_IRQ_CAUSE 0x00
#define ORION_BRIDGE_IRQ_MASK 0x04
-static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
+static void orion_bridge_irq_handler(struct irq_desc *desc)
{
- struct irq_domain *d = irq_get_handler_data(irq);
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) &
@@ -198,8 +197,8 @@ static int __init orion_bridge_irq_init(struct device_node *np,
writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK);
writel(0, gc->reg_base + ORION_BRIDGE_IRQ_CAUSE);
- irq_set_handler_data(irq, domain);
- irq_set_chained_handler(irq, orion_bridge_irq_handler);
+ irq_set_chained_handler_and_data(irq, orion_bridge_irq_handler,
+ domain);
return 0;
}
diff --git a/kernel/drivers/irqchip/irq-renesas-h8300h.c b/kernel/drivers/irqchip/irq-renesas-h8300h.c
new file mode 100644
index 000000000..6fd30d5ee
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-renesas-h8300h.c
@@ -0,0 +1,93 @@
+/*
+ * H8/300H interrupt controller driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+static const char ipr_bit[] = {
+ 7, 6, 5, 5,
+ 4, 4, 4, 4, 3, 3, 3, 3,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 0, 0, 0, 15, 15, 15, 15,
+ 14, 14, 14, 14, 13, 13, 13, 13,
+ -1, -1, -1, -1, 11, 11, 11, 11,
+ 10, 10, 10, 10, 9, 9, 9, 9,
+};
+
+static void *intc_baseaddr;
+
+#define IPR ((unsigned long)intc_baseaddr + 6)
+
+static void h8300h_disable_irq(struct irq_data *data)
+{
+ int bit;
+ int irq = data->irq - 12;
+
+ bit = ipr_bit[irq];
+ if (bit >= 0) {
+ if (bit < 8)
+ ctrl_bclr(bit & 7, IPR);
+ else
+ ctrl_bclr(bit & 7, (IPR+1));
+ }
+}
+
+static void h8300h_enable_irq(struct irq_data *data)
+{
+ int bit;
+ int irq = data->irq - 12;
+
+ bit = ipr_bit[irq];
+ if (bit >= 0) {
+ if (bit < 8)
+ ctrl_bset(bit & 7, IPR);
+ else
+ ctrl_bset(bit & 7, (IPR+1));
+ }
+}
+
+struct irq_chip h8300h_irq_chip = {
+ .name = "H8/300H-INTC",
+ .irq_enable = h8300h_enable_irq,
+ .irq_disable = h8300h_disable_irq,
+};
+
+static int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &h8300h_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init h8300h_intc_of_init(struct device_node *intc,
+ struct device_node *parent)
+{
+ struct irq_domain *domain;
+
+ intc_baseaddr = of_iomap(intc, 0);
+ BUG_ON(!intc_baseaddr);
+
+ /* All interrupt priority low */
+ ctrl_outb(0x00, IPR + 0);
+ ctrl_outb(0x00, IPR + 1);
+
+ domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
+ BUG_ON(!domain);
+ irq_set_default_host(domain);
+ return 0;
+}
+
+IRQCHIP_DECLARE(h8300h_intc, "renesas,h8300h-intc", h8300h_intc_of_init);
diff --git a/kernel/drivers/irqchip/irq-renesas-h8s.c b/kernel/drivers/irqchip/irq-renesas-h8s.c
new file mode 100644
index 000000000..8098ead1e
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-renesas-h8s.c
@@ -0,0 +1,101 @@
+/*
+ * H8S interrupt contoller driver
+ *
+ * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+static void *intc_baseaddr;
+#define IPRA ((unsigned long)intc_baseaddr)
+
+static const unsigned char ipr_table[] = {
+ 0x03, 0x02, 0x01, 0x00, 0x13, 0x12, 0x11, 0x10, /* 16 - 23 */
+ 0x23, 0x22, 0x21, 0x20, 0x33, 0x32, 0x31, 0x30, /* 24 - 31 */
+ 0x43, 0x42, 0x41, 0x40, 0x53, 0x53, 0x52, 0x52, /* 32 - 39 */
+ 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, /* 40 - 47 */
+ 0x50, 0x50, 0x50, 0x50, 0x63, 0x63, 0x63, 0x63, /* 48 - 55 */
+ 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, /* 56 - 63 */
+ 0x61, 0x61, 0x61, 0x61, 0x60, 0x60, 0x60, 0x60, /* 64 - 71 */
+ 0x73, 0x73, 0x73, 0x73, 0x72, 0x72, 0x72, 0x72, /* 72 - 79 */
+ 0x71, 0x71, 0x71, 0x71, 0x70, 0x83, 0x82, 0x81, /* 80 - 87 */
+ 0x80, 0x80, 0x80, 0x80, 0x93, 0x93, 0x93, 0x93, /* 88 - 95 */
+ 0x92, 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, /* 96 - 103 */
+ 0x90, 0x90, 0x90, 0x90, 0xa3, 0xa3, 0xa3, 0xa3, /* 104 - 111 */
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, /* 112 - 119 */
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, /* 120 - 127 */
+};
+
+static void h8s_disable_irq(struct irq_data *data)
+{
+ int pos;
+ unsigned int addr;
+ unsigned short pri;
+ int irq = data->irq;
+
+ addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3);
+ pos = (ipr_table[irq - 16] & 0x0f) * 4;
+ pri = ~(0x000f << pos);
+ pri &= ctrl_inw(addr);
+ ctrl_outw(pri, addr);
+}
+
+static void h8s_enable_irq(struct irq_data *data)
+{
+ int pos;
+ unsigned int addr;
+ unsigned short pri;
+ int irq = data->irq;
+
+ addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3);
+ pos = (ipr_table[irq - 16] & 0x0f) * 4;
+ pri = ~(0x000f << pos);
+ pri &= ctrl_inw(addr);
+ pri |= 1 << pos;
+ ctrl_outw(pri, addr);
+}
+
+struct irq_chip h8s_irq_chip = {
+ .name = "H8S-INTC",
+ .irq_enable = h8s_enable_irq,
+ .irq_disable = h8s_disable_irq,
+};
+
+static __init int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &h8s_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init h8s_intc_of_init(struct device_node *intc,
+ struct device_node *parent)
+{
+ struct irq_domain *domain;
+ int n;
+
+ intc_baseaddr = of_iomap(intc, 0);
+ BUG_ON(!intc_baseaddr);
+
+ /* All interrupt priority is 0 (disable) */
+ /* IPRA to IPRK */
+ for (n = 0; n <= 'k' - 'a'; n++)
+ ctrl_outw(0x0000, IPRA + (n * 2));
+
+ domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
+ BUG_ON(!domain);
+ irq_set_default_host(domain);
+ return 0;
+}
+
+IRQCHIP_DECLARE(h8s_intc, "renesas,h8s-intc", h8s_intc_of_init);
diff --git a/kernel/drivers/irqchip/irq-renesas-intc-irqpin.c b/kernel/drivers/irqchip/irq-renesas-intc-irqpin.c
index 9a0767b9c..c32580656 100644
--- a/kernel/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/kernel/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -283,6 +283,9 @@ static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type)
static int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d);
+ int hw_irq = irqd_to_hwirq(d);
+
+ irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
if (!p->clk)
return 0;
@@ -332,6 +335,12 @@ static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id)
return status;
}
+/*
+ * This lock class tells lockdep that INTC External IRQ Pin irqs are in a
+ * different category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key intc_irqpin_irq_lock_class;
+
static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
@@ -342,24 +351,26 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
intc_irqpin_dbg(&p->irq[hw], "map");
irq_set_chip_data(virq, h->host_data);
+ irq_set_lockdep_class(virq, &intc_irqpin_irq_lock_class);
irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
- set_irq_flags(virq, IRQF_VALID); /* kill me now */
return 0;
}
-static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
+static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
.map = intc_irqpin_irq_domain_map,
.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/kernel/drivers/irqchip/irq-renesas-irqc.c b/kernel/drivers/irqchip/irq-renesas-irqc.c
index cdf80b779..52304b139 100644
--- a/kernel/drivers/irqchip/irq-renesas-irqc.c
+++ b/kernel/drivers/irqchip/irq-renesas-irqc.c
@@ -29,7 +29,6 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/platform_data/irq-renesas-irqc.h>
#include <linux/pm_runtime.h>
#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
@@ -54,7 +53,6 @@
struct irqc_irq {
int hw_irq;
int requested_irq;
- int domain_irq;
struct irqc_priv *p;
};
@@ -62,36 +60,22 @@ struct irqc_priv {
void __iomem *iomem;
void __iomem *cpu_int_base;
struct irqc_irq irq[IRQC_IRQ_MAX];
- struct renesas_irqc_config config;
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)
+static struct irqc_priv *irq_data_to_priv(struct irq_data *data)
{
- dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
- str, i->requested_irq, i->hw_irq, i->domain_irq);
+ return data->domain->host_data;
}
-static void irqc_irq_enable(struct irq_data *d)
-{
- 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);
-}
-
-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] = {
@@ -104,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;
@@ -123,7 +107,10 @@ 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);
if (!p->clk)
return 0;
@@ -147,39 +134,17 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
if (ioread32(p->iomem + DETECT_STATUS) & bit) {
iowrite32(bit, p->iomem + DETECT_STATUS);
irqc_dbg(i, "demux2");
- generic_handle_irq(i->domain_irq);
+ generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq));
return IRQ_HANDLED;
}
return IRQ_NONE;
}
-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;
-
- p->irq[hw].domain_irq = virq;
- p->irq[hw].hw_irq = hw;
-
- irqc_dbg(&p->irq[hw], "map");
- irq_set_chip_data(virq, h->host_data);
- irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
- set_irq_flags(virq, IRQF_VALID); /* kill me now */
- return 0;
-}
-
-static 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 renesas_irqc_config *pdata = pdev->dev.platform_data;
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;
@@ -191,10 +156,6 @@ static int irqc_probe(struct platform_device *pdev)
goto err0;
}
- /* deal with driver instance configuration */
- if (pdata)
- memcpy(&p->config, pdata, sizeof(*pdata));
-
p->pdev = pdev;
platform_set_drvdata(pdev, p);
@@ -222,6 +183,7 @@ static int irqc_probe(struct platform_device *pdev)
break;
p->irq[k].p = p;
+ p->irq[k].hw_irq = k;
p->irq[k].requested_irq = irq->start;
}
@@ -242,48 +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_simple(pdev->dev.of_node,
+ p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
p->number_of_irqs,
- p->config.irq_base,
- &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);
- /* warn in case of mismatch if irq base is specified */
- if (p->config.irq_base) {
- if (p->config.irq_base != p->irq[0].domain_irq)
- dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
- p->config.irq_base, p->irq[0].domain_irq);
- }
-
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/kernel/drivers/irqchip/irq-s3c24xx.c b/kernel/drivers/irqchip/irq-s3c24xx.c
index c8d373fcd..c71914e8f 100644
--- a/kernel/drivers/irqchip/irq-s3c24xx.c
+++ b/kernel/drivers/irqchip/irq-s3c24xx.c
@@ -25,6 +25,7 @@
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -40,8 +41,6 @@
#include <plat/regs-irqtype.h>
#include <plat/pm.h>
-#include "irqchip.h"
-
#define S3C_IRQTYPE_NONE 0
#define S3C_IRQTYPE_EINT 1
#define S3C_IRQTYPE_EDGE 2
@@ -299,22 +298,20 @@ static struct irq_chip s3c_irq_eint0t4 = {
.irq_set_type = s3c_irqext0_type,
};
-static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc)
+static void s3c_irq_demux(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
struct s3c_irq_intc *intc = irq_data->intc;
struct s3c_irq_intc *sub_intc = irq_data->sub_intc;
- unsigned long src;
- unsigned long msk;
- unsigned int n;
- unsigned int offset;
+ unsigned int n, offset, irq;
+ unsigned long src, msk;
/* we're using individual domains for the non-dt case
* 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);
@@ -345,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
@@ -469,13 +466,11 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
irq_set_chip_data(virq, irq_data);
- set_irq_flags(virq, IRQF_VALID);
-
if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) {
if (irq_data->parent_irq > 31) {
pr_err("irq-s3c24xx: parent irq %lu is out of range\n",
irq_data->parent_irq);
- goto err;
+ return -EINVAL;
}
parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
@@ -488,21 +483,15 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
if (!irqno) {
pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n",
irq_data->parent_irq);
- goto err;
+ return -EINVAL;
}
irq_set_chained_handler(irqno, s3c_irq_demux);
}
return 0;
-
-err:
- set_irq_flags(virq, 0);
-
- /* the only error can result from bad mapping data*/
- return -EINVAL;
}
-static struct irq_domain_ops s3c24xx_irq_ops = {
+static const struct irq_domain_ops s3c24xx_irq_ops = {
.map = s3c24xx_irq_map,
.xlate = irq_domain_xlate_twocell,
};
@@ -1177,8 +1166,6 @@ static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
irq_set_chip_data(virq, irq_data);
- set_irq_flags(virq, IRQF_VALID);
-
return 0;
}
@@ -1228,7 +1215,7 @@ static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
return 0;
}
-static struct irq_domain_ops s3c24xx_irq_ops_of = {
+static const struct irq_domain_ops s3c24xx_irq_ops_of = {
.map = s3c24xx_irq_map_of,
.xlate = s3c24xx_irq_xlate_of,
};
diff --git a/kernel/drivers/irqchip/irq-sa11x0.c b/kernel/drivers/irqchip/irq-sa11x0.c
new file mode 100644
index 000000000..61bb28d7b
--- /dev/null
+++ b/kernel/drivers/irqchip/irq-sa11x0.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 Dmitry Eremin-Solenikov
+ * Copyright (C) 1999-2001 Nicolas Pitre
+ *
+ * Generic IRQ handling for the SA11x0.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/syscore_ops.h>
+#include <linux/irqchip/irq-sa11x0.h>
+
+#include <soc/sa1100/pwer.h>
+
+#include <asm/exception.h>
+
+#define ICIP 0x00 /* IC IRQ Pending reg. */
+#define ICMR 0x04 /* IC Mask Reg. */
+#define ICLR 0x08 /* IC Level Reg. */
+#define ICCR 0x0C /* IC Control Reg. */
+#define ICFP 0x10 /* IC FIQ Pending reg. */
+#define ICPR 0x20 /* IC Pending Reg. */
+
+static void __iomem *iobase;
+
+/*
+ * We don't need to ACK IRQs on the SA1100 unless they're GPIOs
+ * this is for internal IRQs i.e. from IRQ LCD to RTCAlrm.
+ */
+static void sa1100_mask_irq(struct irq_data *d)
+{
+ u32 reg;
+
+ reg = readl_relaxed(iobase + ICMR);
+ reg &= ~BIT(d->hwirq);
+ writel_relaxed(reg, iobase + ICMR);
+}
+
+static void sa1100_unmask_irq(struct irq_data *d)
+{
+ u32 reg;
+
+ reg = readl_relaxed(iobase + ICMR);
+ reg |= BIT(d->hwirq);
+ writel_relaxed(reg, iobase + ICMR);
+}
+
+static int sa1100_set_wake(struct irq_data *d, unsigned int on)
+{
+ return sa11x0_sc_set_wake(d->hwirq, on);
+}
+
+static struct irq_chip sa1100_normal_chip = {
+ .name = "SC",
+ .irq_ack = sa1100_mask_irq,
+ .irq_mask = sa1100_mask_irq,
+ .irq_unmask = sa1100_unmask_irq,
+ .irq_set_wake = sa1100_set_wake,
+};
+
+static int sa1100_normal_irqdomain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &sa1100_normal_chip,
+ handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops sa1100_normal_irqdomain_ops = {
+ .map = sa1100_normal_irqdomain_map,
+ .xlate = irq_domain_xlate_onetwocell,
+};
+
+static struct irq_domain *sa1100_normal_irqdomain;
+
+static struct sa1100irq_state {
+ unsigned int saved;
+ unsigned int icmr;
+ unsigned int iclr;
+ unsigned int iccr;
+} sa1100irq_state;
+
+static int sa1100irq_suspend(void)
+{
+ struct sa1100irq_state *st = &sa1100irq_state;
+
+ st->saved = 1;
+ st->icmr = readl_relaxed(iobase + ICMR);
+ st->iclr = readl_relaxed(iobase + ICLR);
+ st->iccr = readl_relaxed(iobase + ICCR);
+
+ /*
+ * Disable all GPIO-based interrupts.
+ */
+ writel_relaxed(st->icmr & 0xfffff000, iobase + ICMR);
+
+ return 0;
+}
+
+static void sa1100irq_resume(void)
+{
+ struct sa1100irq_state *st = &sa1100irq_state;
+
+ if (st->saved) {
+ writel_relaxed(st->iccr, iobase + ICCR);
+ writel_relaxed(st->iclr, iobase + ICLR);
+
+ writel_relaxed(st->icmr, iobase + ICMR);
+ }
+}
+
+static struct syscore_ops sa1100irq_syscore_ops = {
+ .suspend = sa1100irq_suspend,
+ .resume = sa1100irq_resume,
+};
+
+static int __init sa1100irq_init_devicefs(void)
+{
+ register_syscore_ops(&sa1100irq_syscore_ops);
+ return 0;
+}
+
+device_initcall(sa1100irq_init_devicefs);
+
+static asmlinkage void __exception_irq_entry
+sa1100_handle_irq(struct pt_regs *regs)
+{
+ uint32_t icip, icmr, mask;
+
+ do {
+ icip = readl_relaxed(iobase + ICIP);
+ icmr = readl_relaxed(iobase + ICMR);
+ mask = icip & icmr;
+
+ if (mask == 0)
+ break;
+
+ handle_domain_irq(sa1100_normal_irqdomain,
+ ffs(mask) - 1, regs);
+ } while (1);
+}
+
+void __init sa11x0_init_irq_nodt(int irq_start, resource_size_t io_start)
+{
+ iobase = ioremap(io_start, SZ_64K);
+ if (WARN_ON(!iobase))
+ return;
+
+ /* disable all IRQs */
+ writel_relaxed(0, iobase + ICMR);
+
+ /* all IRQs are IRQ, not FIQ */
+ writel_relaxed(0, iobase + ICLR);
+
+ /*
+ * Whatever the doc says, this has to be set for the wait-on-irq
+ * instruction to work... on a SA1100 rev 9 at least.
+ */
+ writel_relaxed(1, iobase + ICCR);
+
+ sa1100_normal_irqdomain = irq_domain_add_simple(NULL,
+ 32, irq_start,
+ &sa1100_normal_irqdomain_ops, NULL);
+
+ set_handle_irq(sa1100_handle_irq);
+}
diff --git a/kernel/drivers/irqchip/irq-sirfsoc.c b/kernel/drivers/irqchip/irq-sirfsoc.c
index a469355df..10cb21b9b 100644
--- a/kernel/drivers/irqchip/irq-sirfsoc.c
+++ b/kernel/drivers/irqchip/irq-sirfsoc.c
@@ -11,40 +11,44 @@
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/syscore_ops.h>
#include <asm/mach/irq.h>
#include <asm/exception.h>
-#include "irqchip.h"
-#define SIRFSOC_INT_RISC_MASK0 0x0018
-#define SIRFSOC_INT_RISC_MASK1 0x001C
-#define SIRFSOC_INT_RISC_LEVEL0 0x0020
-#define SIRFSOC_INT_RISC_LEVEL1 0x0024
+#define SIRFSOC_INT_RISC_MASK0 0x0018
+#define SIRFSOC_INT_RISC_MASK1 0x001C
+#define SIRFSOC_INT_RISC_LEVEL0 0x0020
+#define SIRFSOC_INT_RISC_LEVEL1 0x0024
#define SIRFSOC_INIT_IRQ_ID 0x0038
+#define SIRFSOC_INT_BASE_OFFSET 0x0004
#define SIRFSOC_NUM_IRQS 64
+#define SIRFSOC_NUM_BANKS (SIRFSOC_NUM_IRQS / 32)
static struct irq_domain *sirfsoc_irqdomain;
-static __init void
-sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
+static __init void sirfsoc_alloc_gc(void __iomem *base)
{
- struct irq_chip_generic *gc;
- struct irq_chip_type *ct;
- int ret;
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
unsigned int set = IRQ_LEVEL;
-
- ret = irq_alloc_domain_generic_chips(sirfsoc_irqdomain, num, 1, "irq_sirfsoc",
- handle_level_irq, clr, set, IRQ_GC_INIT_MASK_CACHE);
-
- gc = irq_get_domain_generic_chip(sirfsoc_irqdomain, irq_start);
- gc->reg_base = base;
- ct = gc->chip_types;
- ct->chip.irq_mask = irq_gc_mask_clr_bit;
- ct->chip.irq_unmask = irq_gc_mask_set_bit;
- ct->regs.mask = SIRFSOC_INT_RISC_MASK0;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ int i;
+
+ irq_alloc_domain_generic_chips(sirfsoc_irqdomain, 32, 1, "irq_sirfsoc",
+ handle_level_irq, clr, set,
+ IRQ_GC_INIT_MASK_CACHE);
+
+ for (i = 0; i < SIRFSOC_NUM_BANKS; i++) {
+ gc = irq_get_domain_generic_chip(sirfsoc_irqdomain, i * 32);
+ gc->reg_base = base + i * SIRFSOC_INT_BASE_OFFSET;
+ ct = gc->chip_types;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->regs.mask = SIRFSOC_INT_RISC_MASK0;
+ }
}
static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
@@ -64,10 +68,8 @@ static int __init sirfsoc_irq_init(struct device_node *np,
panic("unable to map intc cpu registers\n");
sirfsoc_irqdomain = irq_domain_add_linear(np, SIRFSOC_NUM_IRQS,
- &irq_generic_chip_ops, base);
-
- sirfsoc_alloc_gc(base, 0, 32);
- sirfsoc_alloc_gc(base + 4, 32, SIRFSOC_NUM_IRQS - 32);
+ &irq_generic_chip_ops, base);
+ sirfsoc_alloc_gc(base);
writel_relaxed(0, base + SIRFSOC_INT_RISC_LEVEL0);
writel_relaxed(0, base + SIRFSOC_INT_RISC_LEVEL1);
diff --git a/kernel/drivers/irqchip/irq-sun4i.c b/kernel/drivers/irqchip/irq-sun4i.c
index 64155b686..0704362f4 100644
--- a/kernel/drivers/irqchip/irq-sun4i.c
+++ b/kernel/drivers/irqchip/irq-sun4i.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -23,8 +24,6 @@
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
#define SUN4I_IRQ_VECTOR_REG 0x00
#define SUN4I_IRQ_PROTECTION_REG 0x08
#define SUN4I_IRQ_NMI_CTRL_REG 0x0c
@@ -84,12 +83,12 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
- set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(virq);
return 0;
}
-static struct irq_domain_ops sun4i_irq_ops = {
+static const struct irq_domain_ops sun4i_irq_ops = {
.map = sun4i_irq_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/kernel/drivers/irqchip/irq-sunxi-nmi.c b/kernel/drivers/irqchip/irq-sunxi-nmi.c
index 6b2b58243..4ef178078 100644
--- a/kernel/drivers/irqchip/irq-sunxi-nmi.c
+++ b/kernel/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>
@@ -17,8 +20,8 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
-#include "irqchip.h"
#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003
@@ -58,10 +61,10 @@ static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
return irq_reg_readl(gc, off);
}
-static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc)
+static void sunxi_sc_nmi_handle_irq(struct irq_desc *desc)
{
struct irq_domain *domain = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_get_chip(irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int virq = irq_find_mapping(domain, 0);
chained_irq_enter(chip, desc);
@@ -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;
}
@@ -182,8 +184,7 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1);
- irq_set_handler_data(irq, domain);
- irq_set_chained_handler(irq, sunxi_sc_nmi_handle_irq);
+ irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain);
return 0;
diff --git a/kernel/drivers/irqchip/irq-tb10x.c b/kernel/drivers/irqchip/irq-tb10x.c
index accc20036..848d782a2 100644
--- a/kernel/drivers/irqchip/irq-tb10x.c
+++ b/kernel/drivers/irqchip/irq-tb10x.c
@@ -22,13 +22,13 @@
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/bitops.h>
-#include "irqchip.h"
#define AB_IRQCTL_INT_ENABLE 0x00
#define AB_IRQCTL_INT_STATUS 0x04
@@ -97,9 +97,10 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type)
return IRQ_SET_MASK_OK;
}
-static void tb10x_irq_cascade(unsigned int irq, struct irq_desc *desc)
+static void tb10x_irq_cascade(struct irq_desc *desc)
{
struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ unsigned int irq = irq_desc_get_irq(desc);
generic_handle_irq(irq_find_mapping(domain, irq));
}
@@ -173,8 +174,8 @@ static int __init of_tb10x_init_irq(struct device_node *ictl,
for (i = 0; i < nrirqs; i++) {
unsigned int irq = irq_of_parse_and_map(ictl, i);
- irq_set_handler_data(irq, domain);
- irq_set_chained_handler(irq, tb10x_irq_cascade);
+ irq_set_chained_handler_and_data(irq, tb10x_irq_cascade,
+ domain);
}
ab_irqctl_writereg(gc, AB_IRQCTL_INT_ENABLE, 0);
diff --git a/kernel/drivers/irqchip/irq-tegra.c b/kernel/drivers/irqchip/irq-tegra.c
index f67bbd804..121ec3013 100644
--- a/kernel/drivers/irqchip/irq-tegra.c
+++ b/kernel/drivers/irqchip/irq-tegra.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/slab.h>
@@ -31,8 +32,6 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
-#include "irqchip.h"
-
#define ICTLR_CPU_IEP_VFIQ 0x08
#define ICTLR_CPU_IEP_FIR 0x14
#define ICTLR_CPU_IEP_FIR_SET 0x18
@@ -215,47 +214,50 @@ static struct irq_chip tegra_ictlr_chip = {
.irq_unmask = tegra_unmask,
.irq_retrigger = tegra_retrigger,
.irq_set_wake = tegra_set_wake,
+ .irq_set_type = irq_chip_set_type_parent,
.flags = IRQCHIP_MASK_ON_SUSPEND,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#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/kernel/drivers/irqchip/irq-versatile-fpga.c b/kernel/drivers/irqchip/irq-versatile-fpga.c
index 1ab451729..cadf104e3 100644
--- a/kernel/drivers/irqchip/irq-versatile-fpga.c
+++ b/kernel/drivers/irqchip/irq-versatile-fpga.c
@@ -4,6 +4,7 @@
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/versatile-fpga.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
@@ -14,8 +15,6 @@
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
#define IRQ_STATUS 0x00
#define IRQ_RAW_STATUS 0x04
#define IRQ_ENABLE_SET 0x08
@@ -66,18 +65,19 @@ static void fpga_irq_unmask(struct irq_data *d)
writel(mask, f->base + IRQ_ENABLE_SET);
}
-static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
+static void fpga_irq_handle(struct irq_desc *desc)
{
struct fpga_irq_data *f = irq_desc_get_handler_data(desc);
u32 status = readl(f->base + IRQ_STATUS);
if (status == 0) {
- do_bad_IRQ(irq, desc);
+ do_bad_IRQ(desc);
return;
}
do {
- irq = ffs(status) - 1;
+ unsigned int irq = ffs(status) - 1;
+
status &= ~(1 << irq);
generic_handle_irq(irq_find_mapping(f->domain, irq));
} while (status);
@@ -128,11 +128,11 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_data(irq, f);
irq_set_chip_and_handler(irq, &f->chip,
handle_level_irq);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
return 0;
}
-static struct irq_domain_ops fpga_irqdomain_ops = {
+static const struct irq_domain_ops fpga_irqdomain_ops = {
.map = fpga_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
@@ -156,8 +156,8 @@ void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
f->valid = valid;
if (parent_irq != -1) {
- irq_set_handler_data(parent_irq, f);
- irq_set_chained_handler(parent_irq, fpga_irq_handle);
+ irq_set_chained_handler_and_data(parent_irq, fpga_irq_handle,
+ f);
}
/* This will also allocate irq descriptors */
@@ -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/kernel/drivers/irqchip/irq-vf610-mscm-ir.c b/kernel/drivers/irqchip/irq-vf610-mscm-ir.c
index 9521057d4..56b5e3cb9 100644
--- a/kernel/drivers/irqchip/irq-vf610-mscm-ir.c
+++ b/kernel/drivers/irqchip/irq-vf610-mscm-ir.c
@@ -26,6 +26,7 @@
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -34,8 +35,6 @@
#include <linux/slab.h>
#include <linux/regmap.h>
-#include "irqchip.h"
-
#define MSCM_CPxNUM 0x4
#define MSCM_IRSPRC(n) (0x80 + 2 * (n))
@@ -47,6 +46,7 @@ struct vf610_mscm_ir_chip_data {
void __iomem *mscm_ir_base;
u16 cpu_mask;
u16 saved_irsprc[MSCM_IRSPRC_NUM];
+ bool is_nvic;
};
static struct vf610_mscm_ir_chip_data *mscm_ir_data;
@@ -101,7 +101,7 @@ static void vf610_mscm_ir_enable(struct irq_data *data)
writew_relaxed(chip_data->cpu_mask,
chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
- irq_chip_unmask_parent(data);
+ irq_chip_enable_parent(data);
}
static void vf610_mscm_ir_disable(struct irq_data *data)
@@ -111,7 +111,7 @@ static void vf610_mscm_ir_disable(struct irq_data *data)
writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
- irq_chip_mask_parent(data);
+ irq_chip_disable_parent(data);
}
static struct irq_chip vf610_mscm_ir_irq_chip = {
@@ -130,28 +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_domain_get_of_node(domain->parent))
+ return -EINVAL;
- if (irq_data->args_count != 2)
+ if (fwspec->param_count != 2)
return -EINVAL;
- hwirq = irq_data->args[0];
+ 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;
- 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];
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+ parent_fwspec.fwnode = domain->parent->fwnode;
+
+ if (mscm_ir_data->is_nvic) {
+ parent_fwspec.param_count = 1;
+ parent_fwspec.param[0] = fwspec->param[0];
+ } else {
+ 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,
+ &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,
};
@@ -174,10 +197,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
return -ENOMEM;
mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
-
- if (!mscm_ir_data->mscm_ir_base) {
+ if (IS_ERR(mscm_ir_data->mscm_ir_base)) {
pr_err("vf610_mscm_ir: unable to map mscm register\n");
- ret = -ENOMEM;
+ ret = PTR_ERR(mscm_ir_data->mscm_ir_base);
goto out_free;
}
@@ -199,6 +221,10 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
goto out_unmap;
}
+ 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);
return 0;
diff --git a/kernel/drivers/irqchip/irq-vic.c b/kernel/drivers/irqchip/irq-vic.c
index 54089debf..b956dfffe 100644
--- a/kernel/drivers/irqchip/irq-vic.c
+++ b/kernel/drivers/irqchip/irq-vic.c
@@ -24,6 +24,7 @@
#include <linux/list.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
@@ -37,8 +38,6 @@
#include <asm/exception.h>
#include <asm/irq.h>
-#include "irqchip.h"
-
#define VIC_IRQ_STATUS 0x00
#define VIC_FIQ_STATUS 0x04
#define VIC_INT_SELECT 0x0c /* 1 = FIQ, 0 = IRQ */
@@ -202,7 +201,7 @@ static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
return -EPERM;
irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);
irq_set_chip_data(irq, v->base);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ irq_set_probe(irq);
return 0;
}
@@ -226,7 +225,7 @@ 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)
+static void vic_handle_irq_cascaded(struct irq_desc *desc)
{
u32 stat, hwirq;
struct irq_chip *host_chip = irq_desc_get_chip(desc);
@@ -256,7 +255,7 @@ static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
} while (handled);
}
-static struct irq_domain_ops vic_irqdomain_ops = {
+static const struct irq_domain_ops vic_irqdomain_ops = {
.map = vic_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
@@ -297,8 +296,8 @@ static void __init vic_register(void __iomem *base, unsigned int parent_irq,
vic_id++;
if (parent_irq) {
- irq_set_handler_data(parent_irq, v);
- irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
+ irq_set_chained_handler_and_data(parent_irq,
+ vic_handle_irq_cascaded, v);
}
v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
diff --git a/kernel/drivers/irqchip/irq-vt8500.c b/kernel/drivers/irqchip/irq-vt8500.c
index b7af816f2..f9af0af21 100644
--- a/kernel/drivers/irqchip/irq-vt8500.c
+++ b/kernel/drivers/irqchip/irq-vt8500.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
@@ -39,8 +40,6 @@
#include <asm/exception.h>
#include <asm/mach/irq.h>
-#include "irqchip.h"
-
#define VT8500_ICPC_IRQ 0x20
#define VT8500_ICPC_FIQ 0x24
#define VT8500_ICDC 0x40 /* Destination Control 64*u32 */
@@ -127,15 +126,15 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
return -EINVAL;
case IRQF_TRIGGER_HIGH:
dctr |= VT8500_TRIGGER_HIGH;
- __irq_set_handler_locked(d->irq, handle_level_irq);
+ irq_set_handler_locked(d, handle_level_irq);
break;
case IRQF_TRIGGER_FALLING:
dctr |= VT8500_TRIGGER_FALLING;
- __irq_set_handler_locked(d->irq, handle_edge_irq);
+ irq_set_handler_locked(d, handle_edge_irq);
break;
case IRQF_TRIGGER_RISING:
dctr |= VT8500_TRIGGER_RISING;
- __irq_set_handler_locked(d->irq, handle_edge_irq);
+ irq_set_handler_locked(d, handle_edge_irq);
break;
}
writeb(dctr, base + VT8500_ICDC + d->hwirq);
@@ -168,12 +167,11 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq);
- set_irq_flags(virq, IRQF_VALID);
return 0;
}
-static struct irq_domain_ops vt8500_irq_domain_ops = {
+static const struct irq_domain_ops vt8500_irq_domain_ops = {
.map = vt8500_irq_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/kernel/drivers/irqchip/irq-xtensa-mx.c b/kernel/drivers/irqchip/irq-xtensa-mx.c
index e1c2f9632..bb3ac5fe5 100644
--- a/kernel/drivers/irqchip/irq-xtensa-mx.c
+++ b/kernel/drivers/irqchip/irq-xtensa-mx.c
@@ -11,12 +11,11 @@
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <asm/mxregs.h>
-#include "irqchip.h"
-
#define HW_IRQ_IPI_COUNT 2
#define HW_IRQ_MX_BASE 2
#define HW_IRQ_EXTERN_BASE 3
diff --git a/kernel/drivers/irqchip/irq-xtensa-pic.c b/kernel/drivers/irqchip/irq-xtensa-pic.c
index 7d71126d1..472ae1770 100644
--- a/kernel/drivers/irqchip/irq-xtensa-pic.c
+++ b/kernel/drivers/irqchip/irq-xtensa-pic.c
@@ -15,10 +15,9 @@
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
-#include "irqchip.h"
-
unsigned int cached_irq_mask;
/*
diff --git a/kernel/drivers/irqchip/irq-zevio.c b/kernel/drivers/irqchip/irq-zevio.c
index e4ef74ed4..4c48fa88a 100644
--- a/kernel/drivers/irqchip/irq-zevio.c
+++ b/kernel/drivers/irqchip/irq-zevio.c
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -18,8 +19,6 @@
#include <asm/mach/irq.h>
#include <asm/exception.h>
-#include "irqchip.h"
-
#define IO_STATUS 0x000
#define IO_RAW_STATUS 0x004
#define IO_ENABLE 0x008
diff --git a/kernel/drivers/irqchip/irqchip.c b/kernel/drivers/irqchip/irqchip.c
index afd1af3df..2b35e68be 100644
--- a/kernel/drivers/irqchip/irqchip.c
+++ b/kernel/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);
}
diff --git a/kernel/drivers/irqchip/irqchip.h b/kernel/drivers/irqchip/irqchip.h
deleted file mode 100644
index 0f6486d4f..000000000
--- a/kernel/drivers/irqchip/irqchip.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2012 Thomas Petazzoni
- *
- * Thomas Petazzoni <thomas.petazzoni@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 _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
- * initialization function.
- *
- * @name: name that must be unique accross all IRQCHIP_DECLARE of the
- * same file.
- * @compstr: compatible string of the irqchip driver
- * @fn: initialization function
- */
-#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
-
-#endif
diff --git a/kernel/drivers/irqchip/spear-shirq.c b/kernel/drivers/irqchip/spear-shirq.c
index 9c145a7cb..1ccd2abed 100644
--- a/kernel/drivers/irqchip/spear-shirq.c
+++ b/kernel/drivers/irqchip/spear-shirq.c
@@ -2,7 +2,7 @@
* SPEAr platform shared irq layer source file
*
* Copyright (C) 2009-2012 ST Microelectronics
- * Viresh Kumar <viresh.linux@gmail.com>
+ * Viresh Kumar <vireshk@kernel.org>
*
* Copyright (C) 2012 ST Microelectronics
* Shiraz Hashim <shiraz.linux.kernel@gmail.com>
@@ -18,14 +18,13 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
-#include "irqchip.h"
-
/*
* struct spear_shirq: shared irq structure
*
@@ -183,9 +182,9 @@ static struct spear_shirq *spear320_shirq_blocks[] = {
&spear320_shirq_intrcomm_ras,
};
-static void shirq_handler(unsigned irq, struct irq_desc *desc)
+static void shirq_handler(struct irq_desc *desc)
{
- struct spear_shirq *shirq = irq_get_handler_data(irq);
+ struct spear_shirq *shirq = irq_desc_get_handler_data(desc);
u32 pend;
pend = readl(shirq->base + shirq->status_reg) & shirq->mask;
@@ -207,13 +206,11 @@ static void __init spear_shirq_register(struct spear_shirq *shirq,
if (!shirq->irq_chip)
return;
- irq_set_chained_handler(parent_irq, shirq_handler);
- irq_set_handler_data(parent_irq, shirq);
+ irq_set_chained_handler_and_data(parent_irq, shirq_handler, 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);
}
}