summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-09 06:42:04 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-09 06:42:04 -0400
commit782d59c5dfc5ac39ac8cfb4c6dd40597938dde9c (patch)
tree2d831c436a1962bfe5dfdb3afeaf87c7a3e82132 /drivers
parent47137c6ba1bcde30215795f9594cea770946456b (diff)
parent2828c9cdb8bd30f49c48210c014ccdd4cb994931 (diff)
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner: "The irq departement delivers: - a cleanup series to get rid of mindlessly copied code. - another bunch of new pointlessly different interrupt chip drivers. Adding homebrewn irq chips (and timers) to SoCs must provide a value add which is beyond the imagination of mere mortals. - the usual SoC irq controller updates, IOW my second cat herding project" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (44 commits) irqchip: gic-v3: Implement CPU PM notifier irqchip: gic-v3: Refactor gic_enable_redist to support both enabling and disabling irqchip: renesas-intc-irqpin: Add minimal runtime PM support irqchip: renesas-intc-irqpin: Add helper variable dev = &pdev->dev irqchip: atmel-aic5: Add sama5d4 support irqchip: atmel-aic5: The sama5d3 has 48 IRQs Documentation: bcm7120-l2: Add Broadcom BCM7120-style L2 binding irqchip: bcm7120-l2: Add Broadcom BCM7120-style Level 2 interrupt controller irqchip: renesas-irqc: Add binding docs for new R-Car Gen2 SoCs irqchip: renesas-irqc: Add DT binding documentation irqchip: renesas-intc-irqpin: Document SoC-specific bindings openrisc: Get rid of handle_IRQ arm64: Get rid of handle_IRQ ARM: omap2: irq: Convert to handle_domain_irq ARM: imx: tzic: Convert to handle_domain_irq ARM: imx: avic: Convert to handle_domain_irq irqchip: or1k-pic: Convert to handle_domain_irq irqchip: atmel-aic5: Convert to handle_domain_irq irqchip: atmel-aic: Convert to handle_domain_irq irqchip: gic-v3: Convert to handle_domain_irq ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/irqchip/Kconfig7
-rw-r--r--drivers/irqchip/Makefile5
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c19
-rw-r--r--drivers/irqchip/irq-atmel-aic.c4
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c16
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c219
-rw-r--r--drivers/irqchip/irq-clps711x.c18
-rw-r--r--drivers/irqchip/irq-gic-common.c15
-rw-r--r--drivers/irqchip/irq-gic-v3.c100
-rw-r--r--drivers/irqchip/irq-gic.c49
-rw-r--r--drivers/irqchip/irq-hip04.c424
-rw-r--r--drivers/irqchip/irq-keystone.c232
-rw-r--r--drivers/irqchip/irq-mmp.c10
-rw-r--r--drivers/irqchip/irq-mxs.c3
-rw-r--r--drivers/irqchip/irq-omap-intc.c3
-rw-r--r--drivers/irqchip/irq-or1k-pic.c4
-rw-r--r--drivers/irqchip/irq-orion.c5
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c85
-rw-r--r--drivers/irqchip/irq-s3c24xx.c4
-rw-r--r--drivers/irqchip/irq-sirfsoc.c6
-rw-r--r--drivers/irqchip/irq-sun4i.c5
-rw-r--r--drivers/irqchip/irq-versatile-fpga.c2
-rw-r--r--drivers/irqchip/irq-vic.c2
-rw-r--r--drivers/irqchip/irq-vt8500.c5
-rw-r--r--drivers/irqchip/irq-zevio.c3
25 files changed, 1107 insertions, 138 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 78d4ff551590..b21f12f1766d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -118,3 +118,10 @@ config IRQ_CROSSBAR
The primary irqchip invokes the crossbar's callback which inturn allocates
a free irq and configures the IP. Thus the peripheral interrupts are
routed to one of the free irqchip interrupt lines.
+
+config KEYSTONE_IRQ
+ tristate "Keystone 2 IRQ controller IP"
+ depends on ARCH_KEYSTONE
+ help
+ Support for Texas Instruments Keystone 2 IRQ controller IP which
+ is part of the Keystone 2 IPC mechanism
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index d0a2613c73bc..173bb5fa2cc9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.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
@@ -34,4 +35,6 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
-obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
+obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \
+ irq-bcm7120-l2.o
+obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 574aba0eba4e..fa75a29a0408 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -393,13 +393,15 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
if (!(msimask & BIT(msinr)))
continue;
- irq = irq_find_mapping(armada_370_xp_msi_domain,
- msinr - 16);
-
- if (is_chained)
+ if (is_chained) {
+ irq = irq_find_mapping(armada_370_xp_msi_domain,
+ msinr - 16);
generic_handle_irq(irq);
- else
- handle_IRQ(irq, regs);
+ } else {
+ irq = msinr - 16;
+ handle_domain_irq(armada_370_xp_msi_domain,
+ irq, regs);
+ }
}
}
#else
@@ -444,9 +446,8 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
break;
if (irqnr > 1) {
- irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
- irqnr);
- handle_IRQ(irqnr, regs);
+ handle_domain_irq(armada_370_xp_mpic_domain,
+ irqnr, regs);
continue;
}
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
index a82869e9fb26..9a2cf3c1a3a5 100644
--- a/drivers/irqchip/irq-atmel-aic.c
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -68,12 +68,10 @@ aic_handle(struct pt_regs *regs)
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
- irqnr = irq_find_mapping(aic_domain, irqnr);
-
if (!irqstat)
irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
else
- handle_IRQ(irqnr, regs);
+ handle_domain_irq(aic_domain, irqnr, regs);
}
static int aic_retrigger(struct irq_data *d)
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index edb227081524..a11aae8fb006 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -78,12 +78,10 @@ aic5_handle(struct pt_regs *regs)
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
- irqnr = irq_find_mapping(aic5_domain, irqnr);
-
if (!irqstat)
irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
else
- handle_IRQ(irqnr, regs);
+ handle_domain_irq(aic5_domain, irqnr, regs);
}
static void aic5_mask(struct irq_data *d)
@@ -297,6 +295,7 @@ static void __init sama5d3_aic_irq_fixup(struct device_node *root)
static const struct of_device_id __initdata aic5_irq_fixups[] = {
{ .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
+ { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
{ /* sentinel */ },
};
@@ -343,7 +342,7 @@ static int __init aic5_of_init(struct device_node *node,
return 0;
}
-#define NR_SAMA5D3_IRQS 50
+#define NR_SAMA5D3_IRQS 48
static int __init sama5d3_aic5_of_init(struct device_node *node,
struct device_node *parent)
@@ -351,3 +350,12 @@ static int __init sama5d3_aic5_of_init(struct device_node *node,
return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
}
IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
+
+#define NR_SAMA5D4_IRQS 68
+
+static int __init sama5d4_aic5_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
+}
+IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init);
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
new file mode 100644
index 000000000000..b9f4fb808e49
--- /dev/null
+++ b/drivers/irqchip/irq-bcm7120-l2.c
@@ -0,0 +1,219 @@
+/*
+ * Broadcom BCM7120 style Level 2 interrupt controller driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/reboot.h>
+#include <linux/irqchip/chained_irq.h>
+
+#include "irqchip.h"
+
+#include <asm/mach/irq.h>
+
+/* Register offset in the L2 interrupt controller */
+#define IRQEN 0x00
+#define IRQSTAT 0x04
+
+struct bcm7120_l2_intc_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ bool can_wake;
+ u32 irq_fwd_mask;
+ u32 irq_map_mask;
+ u32 saved_mask;
+};
+
+static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
+{
+ struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+
+ status = __raw_readl(b->base + IRQSTAT);
+
+ if (status == 0) {
+ do_bad_IRQ(irq, desc);
+ goto out;
+ }
+
+ do {
+ irq = ffs(status) - 1;
+ status &= ~(1 << irq);
+ generic_handle_irq(irq_find_mapping(b->domain, irq));
+ } while (status);
+
+out:
+ chained_irq_exit(chip, desc);
+}
+
+static void bcm7120_l2_intc_suspend(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct bcm7120_l2_intc_data *b = gc->private;
+ u32 reg;
+
+ irq_gc_lock(gc);
+ /* Save the current mask and the interrupt forward mask */
+ b->saved_mask = __raw_readl(b->base) | b->irq_fwd_mask;
+ if (b->can_wake) {
+ reg = b->saved_mask | gc->wake_active;
+ __raw_writel(reg, b->base);
+ }
+ irq_gc_unlock(gc);
+}
+
+static void bcm7120_l2_intc_resume(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct bcm7120_l2_intc_data *b = gc->private;
+
+ /* Restore the saved mask */
+ irq_gc_lock(gc);
+ __raw_writel(b->saved_mask, b->base);
+ irq_gc_unlock(gc);
+}
+
+static int bcm7120_l2_intc_init_one(struct device_node *dn,
+ struct bcm7120_l2_intc_data *data,
+ int irq, const __be32 *map_mask)
+{
+ int parent_irq;
+
+ parent_irq = irq_of_parse_and_map(dn, irq);
+ if (parent_irq < 0) {
+ pr_err("failed to map interrupt %d\n", irq);
+ return parent_irq;
+ }
+
+ data->irq_map_mask |= be32_to_cpup(map_mask + irq);
+
+ irq_set_handler_data(parent_irq, data);
+ irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
+
+ return 0;
+}
+
+int __init bcm7120_l2_intc_of_init(struct device_node *dn,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct bcm7120_l2_intc_data *data;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ const __be32 *map_mask;
+ int num_parent_irqs;
+ int ret = 0, len, irq;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->base = of_iomap(dn, 0);
+ if (!data->base) {
+ pr_err("failed to remap intc L2 registers\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask))
+ data->irq_fwd_mask = 0;
+
+ /* Enable all interrupt specified in the interrupt forward mask and have
+ * the other disabled
+ */
+ __raw_writel(data->irq_fwd_mask, data->base + IRQEN);
+
+ num_parent_irqs = of_irq_count(dn);
+ if (num_parent_irqs <= 0) {
+ pr_err("invalid number of parent interrupts\n");
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
+ if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) {
+ pr_err("invalid brcm,int-map-mask property\n");
+ ret = -EINVAL;
+ goto out_unmap;
+ }
+
+ for (irq = 0; irq < num_parent_irqs; irq++) {
+ ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask);
+ if (ret)
+ goto out_unmap;
+ }
+
+ data->domain = irq_domain_add_linear(dn, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!data->domain) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
+ dn->full_name, handle_level_irq, clr, 0,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("failed to allocate generic irq chip\n");
+ goto out_free_domain;
+ }
+
+ gc = irq_get_domain_generic_chip(data->domain, 0);
+ gc->unused = 0xfffffff & ~data->irq_map_mask;
+ gc->reg_base = data->base;
+ gc->private = data;
+ ct = gc->chip_types;
+
+ ct->regs.mask = IRQEN;
+ 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;
+
+ if (of_property_read_bool(dn, "brcm,irq-can-wake")) {
+ data->can_wake = true;
+ /* This IRQ chip can wake the system, set all relevant child
+ * interupts in wake_enabled mask
+ */
+ gc->wake_enabled = 0xffffffff;
+ gc->wake_enabled &= ~gc->unused;
+ ct->chip.irq_set_wake = irq_gc_set_wake;
+ }
+
+ pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
+ data->base, num_parent_irqs);
+
+ return 0;
+
+out_free_domain:
+ irq_domain_remove(data->domain);
+out_unmap:
+ iounmap(data->base);
+out_free:
+ kfree(data);
+ return ret;
+}
+IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc",
+ bcm7120_l2_intc_of_init);
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
index 33340dc97d1d..33127f131d78 100644
--- a/drivers/irqchip/irq-clps711x.c
+++ b/drivers/irqchip/irq-clps711x.c
@@ -76,24 +76,20 @@ static struct {
static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
{
- u32 irqnr, irqstat;
+ u32 irqstat;
do {
irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
readw_relaxed(clps711x_intc->intsr[0]);
- if (irqstat) {
- irqnr = irq_find_mapping(clps711x_intc->domain,
- fls(irqstat) - 1);
- handle_IRQ(irqnr, regs);
- }
+ if (irqstat)
+ handle_domain_irq(clps711x_intc->domain,
+ fls(irqstat) - 1, regs);
irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
readw_relaxed(clps711x_intc->intsr[1]);
- if (irqstat) {
- irqnr = irq_find_mapping(clps711x_intc->domain,
- fls(irqstat) - 1 + 16);
- handle_IRQ(irqnr, regs);
- }
+ if (irqstat)
+ handle_domain_irq(clps711x_intc->domain,
+ fls(irqstat) - 1 + 16, regs);
} while (irqstat);
}
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index 60ac704d2090..61541ff24397 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -74,20 +74,22 @@ void __init gic_dist_config(void __iomem *base, int gic_irqs,
* Set all global interrupts to be level triggered, active low.
*/
for (i = 32; i < gic_irqs; i += 16)
- writel_relaxed(0, base + GIC_DIST_CONFIG + i / 4);
+ writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
+ base + GIC_DIST_CONFIG + i / 4);
/*
* Set priority on all global interrupts.
*/
for (i = 32; i < gic_irqs; i += 4)
- writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i);
+ 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.
*/
for (i = 32; i < gic_irqs; i += 32)
- writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i / 8);
+ writel_relaxed(GICD_INT_EN_CLR_X32,
+ base + GIC_DIST_ENABLE_CLEAR + i / 8);
if (sync_access)
sync_access();
@@ -101,14 +103,15 @@ 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.
*/
- writel_relaxed(0xffff0000, base + GIC_DIST_ENABLE_CLEAR);
- writel_relaxed(0x0000ffff, base + GIC_DIST_ENABLE_SET);
+ writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
+ writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4)
- writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+ writel_relaxed(GICD_INT_DEF_PRI_X4,
+ base + GIC_DIST_PRI + i * 4 / 4);
if (sync_access)
sync_access();
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index a0698b4f0303..aa17ae805a70 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -16,6 +16,7 @@
*/
#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/of.h>
@@ -155,7 +156,7 @@ static void gic_enable_sre(void)
pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
}
-static void gic_enable_redist(void)
+static void gic_enable_redist(bool enable)
{
void __iomem *rbase;
u32 count = 1000000; /* 1s! */
@@ -163,20 +164,30 @@ static void gic_enable_redist(void)
rbase = gic_data_rdist_rd_base();
- /* Wake up this CPU redistributor */
val = readl_relaxed(rbase + GICR_WAKER);
- val &= ~GICR_WAKER_ProcessorSleep;
+ if (enable)
+ /* Wake up this CPU redistributor */
+ val &= ~GICR_WAKER_ProcessorSleep;
+ else
+ val |= GICR_WAKER_ProcessorSleep;
writel_relaxed(val, rbase + GICR_WAKER);
- while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) {
- count--;
- if (!count) {
- pr_err_ratelimited("redist didn't wake up...\n");
- return;
- }
+ if (!enable) { /* Check that GICR_WAKER is writeable */
+ val = readl_relaxed(rbase + GICR_WAKER);
+ if (!(val & GICR_WAKER_ProcessorSleep))
+ return; /* No PM support in this redistributor */
+ }
+
+ while (count--) {
+ val = readl_relaxed(rbase + GICR_WAKER);
+ if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
+ break;
cpu_relax();
udelay(1);
};
+ if (!count)
+ pr_err_ratelimited("redistributor failed to %s...\n",
+ enable ? "wakeup" : "sleep");
}
/*
@@ -261,14 +272,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
irqnr = gic_read_iar();
if (likely(irqnr > 15 && irqnr < 1020)) {
- u64 irq = irq_find_mapping(gic_data.domain, irqnr);
- if (likely(irq)) {
- handle_IRQ(irq, regs);
- continue;
+ int err;
+ err = handle_domain_irq(gic_data.domain, irqnr, regs);
+ if (err) {
+ WARN_ONCE(true, "Unexpected SPI received!\n");
+ gic_write_eoir(irqnr);
}
-
- WARN_ONCE(true, "Unexpected SPI received!\n");
- gic_write_eoir(irqnr);
+ continue;
}
if (irqnr < 16) {
gic_write_eoir(irqnr);
@@ -360,6 +370,21 @@ static int gic_populate_rdist(void)
return -ENODEV;
}
+static void gic_cpu_sys_reg_init(void)
+{
+ /* Enable system registers */
+ gic_enable_sre();
+
+ /* Set priority mask register */
+ gic_write_pmr(DEFAULT_PMR_VALUE);
+
+ /* EOI deactivates interrupt too (mode 0) */
+ gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+
+ /* ... and let's hit the road... */
+ gic_write_grpen1(1);
+}
+
static void gic_cpu_init(void)
{
void __iomem *rbase;
@@ -368,23 +393,14 @@ static void gic_cpu_init(void)
if (gic_populate_rdist())
return;
- gic_enable_redist();
+ gic_enable_redist(true);
rbase = gic_data_rdist_sgi_base();
gic_cpu_config(rbase, gic_redist_wait_for_rwp);
- /* Enable system registers */
- gic_enable_sre();
-
- /* Set priority mask register */
- gic_write_pmr(DEFAULT_PMR_VALUE);
-
- /* EOI deactivates interrupt too (mode 0) */
- gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
-
- /* ... and let's hit the road... */
- gic_write_grpen1(1);
+ /* initialise system registers */
+ gic_cpu_sys_reg_init();
}
#ifdef CONFIG_SMP
@@ -533,6 +549,33 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
#define gic_smp_init() do { } while(0)
#endif
+#ifdef CONFIG_CPU_PM
+static int gic_cpu_pm_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ if (cmd == CPU_PM_EXIT) {
+ gic_enable_redist(true);
+ gic_cpu_sys_reg_init();
+ } else if (cmd == CPU_PM_ENTER) {
+ gic_write_grpen1(0);
+ gic_enable_redist(false);
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block gic_cpu_pm_notifier_block = {
+ .notifier_call = gic_cpu_pm_notifier,
+};
+
+static void gic_cpu_pm_init(void)
+{
+ cpu_pm_register_notifier(&gic_cpu_pm_notifier_block);
+}
+
+#else
+static inline void gic_cpu_pm_init(void) { }
+#endif /* CONFIG_CPU_PM */
+
static struct irq_chip gic_chip = {
.name = "GICv3",
.irq_mask = gic_mask_irq,
@@ -672,6 +715,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
gic_smp_init();
gic_dist_init();
gic_cpu_init();
+ gic_cpu_pm_init();
return 0;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index dda6dbc23565..f0a4800a15b0 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -270,8 +270,7 @@ 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)) {
- irqnr = irq_find_mapping(gic->domain, irqnr);
- handle_IRQ(irqnr, regs);
+ handle_domain_irq(gic->domain, irqnr, regs);
continue;
}
if (irqnr < 16) {
@@ -298,8 +297,8 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK);
raw_spin_unlock(&irq_controller_lock);
- gic_irq = (status & 0x3ff);
- if (gic_irq == 1023)
+ gic_irq = (status & GICC_IAR_INT_ID_MASK);
+ if (gic_irq == GICC_INT_SPURIOUS)
goto out;
cascade_irq = irq_find_mapping(chip_data->domain, gic_irq);
@@ -353,6 +352,21 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
return mask;
}
+static void gic_cpu_if_up(void)
+{
+ void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
+ u32 bypass = 0;
+
+ /*
+ * Preserve bypass disable bits to be written back later
+ */
+ bypass = readl(cpu_base + GIC_CPU_CTRL);
+ bypass &= GICC_DIS_BYPASS_MASK;
+
+ writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
+}
+
+
static void __init gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i;
@@ -360,7 +374,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
unsigned int gic_irqs = gic->gic_irqs;
void __iomem *base = gic_data_dist_base(gic);
- writel_relaxed(0, base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL);
/*
* Set all global interrupts to this CPU only.
@@ -373,7 +387,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
gic_dist_config(base, gic_irqs, NULL);
- writel_relaxed(1, base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
}
static void gic_cpu_init(struct gic_chip_data *gic)
@@ -400,14 +414,18 @@ static void gic_cpu_init(struct gic_chip_data *gic)
gic_cpu_config(dist_base, NULL);
- writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
- writel_relaxed(1, base + GIC_CPU_CTRL);
+ writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
+ gic_cpu_if_up();
}
void gic_cpu_if_down(void)
{
void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
- writel_relaxed(0, cpu_base + GIC_CPU_CTRL);
+ u32 val = 0;
+
+ val = readl(cpu_base + GIC_CPU_CTRL);
+ val &= ~GICC_ENABLE;
+ writel_relaxed(val, cpu_base + GIC_CPU_CTRL);
}
#ifdef CONFIG_CPU_PM
@@ -467,14 +485,14 @@ static void gic_dist_restore(unsigned int gic_nr)
if (!dist_base)
return;
- writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL);
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
dist_base + GIC_DIST_CONFIG + i * 4);
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
- writel_relaxed(0xa0a0a0a0,
+ writel_relaxed(GICD_INT_DEF_PRI_X4,
dist_base + GIC_DIST_PRI + i * 4);
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
@@ -485,7 +503,7 @@ static void gic_dist_restore(unsigned int gic_nr)
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);
- writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
}
static void gic_cpu_save(unsigned int gic_nr)
@@ -539,10 +557,11 @@ static void gic_cpu_restore(unsigned int gic_nr)
writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
- writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
+ writel_relaxed(GICD_INT_DEF_PRI_X4,
+ dist_base + GIC_DIST_PRI + i * 4);
- writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
- writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+ writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK);
+ gic_cpu_if_up();
}
static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
new file mode 100644
index 000000000000..9c8f833522e6
--- /dev/null
+++ b/drivers/irqchip/irq-hip04.c
@@ -0,0 +1,424 @@
+/*
+ * Hisilicon HiP04 INTC
+ *
+ * Copyright (C) 2002-2014 ARM Limited.
+ * Copyright (c) 2013-2014 Hisilicon Ltd.
+ * Copyright (c) 2013-2014 Linaro Ltd.
+ *
+ * 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.
+ *
+ * Interrupt architecture for the HIP04 INTC:
+ *
+ * o There is one Interrupt Distributor, which receives interrupts
+ * from system devices and sends them to the Interrupt Controllers.
+ *
+ * o There is one CPU Interface per CPU, which sends interrupts sent
+ * by the Distributor, and interrupts generated locally, to the
+ * associated CPU. The base address of the CPU interface is usually
+ * aliased so that the same address points to different chips depending
+ * on the CPU it is accessed from.
+ *
+ * Note that IRQs 0-31 are special - they are local to each CPU.
+ * As such, the enable set/clear, pending set/clear and active bit
+ * registers are banked per-cpu for these sources.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irqchip/arm-gic.h>
+
+#include <asm/irq.h>
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+
+#include "irq-gic-common.h"
+#include "irqchip.h"
+
+#define HIP04_MAX_IRQS 510
+
+struct hip04_irq_data {
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+ struct irq_domain *domain;
+ unsigned int nr_irqs;
+};
+
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+/*
+ * The GIC mapping of CPU interfaces does not necessarily match
+ * the logical CPU numbering. Let's use a mapping as returned
+ * by the GIC itself.
+ */
+#define NR_HIP04_CPU_IF 16
+static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly;
+
+static struct hip04_irq_data hip04_data __read_mostly;
+
+static inline void __iomem *hip04_dist_base(struct irq_data *d)
+{
+ struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+ return hip04_data->dist_base;
+}
+
+static inline void __iomem *hip04_cpu_base(struct irq_data *d)
+{
+ struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
+ return hip04_data->cpu_base;
+}
+
+static inline unsigned int hip04_irq(struct irq_data *d)
+{
+ return d->hwirq;
+}
+
+/*
+ * Routines to acknowledge, disable and enable interrupts
+ */
+static void hip04_mask_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (hip04_irq(d) % 32);
+
+ raw_spin_lock(&irq_controller_lock);
+ writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR +
+ (hip04_irq(d) / 32) * 4);
+ raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_unmask_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (hip04_irq(d) % 32);
+
+ raw_spin_lock(&irq_controller_lock);
+ writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET +
+ (hip04_irq(d) / 32) * 4);
+ raw_spin_unlock(&irq_controller_lock);
+}
+
+static void hip04_eoi_irq(struct irq_data *d)
+{
+ writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI);
+}
+
+static int hip04_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ void __iomem *base = hip04_dist_base(d);
+ unsigned int irq = hip04_irq(d);
+
+ /* Interrupt configuration for SGIs can't be changed */
+ if (irq < 16)
+ return -EINVAL;
+