diff options
45 files changed, 1356 insertions, 203 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt index 2742e9cfd6b1..f292917fa00d 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt @@ -2,7 +2,7 @@ Required properties: - compatible: Should be "atmel,<chip>-aic" - <chip> can be "at91rm9200" or "sama5d3" + <chip> can be "at91rm9200", "sama5d3" or "sama5d4" - interrupt-controller: Identifies the node as an interrupt controller. - interrupt-parent: For single AIC system, it is an empty property. - #interrupt-cells: The number of cells to define the interrupts. It should be 3. diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt new file mode 100644 index 000000000000..ff812a8a82bc --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt @@ -0,0 +1,86 @@ +Broadcom BCM7120-style Level 2 interrupt controller + +This interrupt controller hardware is a second level interrupt controller that +is hooked to a parent interrupt controller: e.g: ARM GIC for ARM-based +platforms. It can be found on BCM7xxx products starting with BCM7120. + +Such an interrupt controller has the following hardware design: + +- outputs multiple interrupts signals towards its interrupt controller parent + +- controls how some of the interrupts will be flowing, whether they will + directly output an interrupt signal towards the interrupt controller parent, + or if they will output an interrupt signal at this 2nd level interrupt + controller, in particular for UARTs + +- not all 32-bits within the interrupt controller actually map to an interrupt + +The typical hardware layout for this controller is represented below: + +2nd level interrupt line Outputs for the parent controller (e.g: ARM GIC) + +0 -----[ MUX ] ------------|==========> GIC interrupt 75 + \-----------\ + | +1 -----[ MUX ] --------)---|==========> GIC interrupt 76 + \------------| + | +2 -----[ MUX ] --------)---|==========> GIC interrupt 77 + \------------| + | +3 ---------------------| +4 ---------------------| +5 ---------------------| +7 ---------------------|---|===========> GIC interrupt 66 +9 ---------------------| +10 --------------------| +11 --------------------/ + +6 ------------------------\ + |===========> GIC interrupt 64 +8 ------------------------/ + +12 ........................ X +13 ........................ X (not connected) +.. +31 ........................ X + +Required properties: + +- compatible: should be "brcm,bcm7120-l2-intc" +- reg: specifies the base physical address and size of the registers +- interrupt-controller: identifies the node as an interrupt controller +- #interrupt-cells: specifies the number of cells needed to encode an interrupt + source, should be 1. +- interrupt-parent: specifies the phandle to the parent interrupt controller + this one is cascaded from +- interrupts: specifies the interrupt line(s) in the interrupt-parent controller + node, valid values depend on the type of parent interrupt controller +- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts + are wired to this 2nd level interrupt controller, and how they match their + respective interrupt parents. Should match exactly the number of interrupts + specified in the 'interrupts' property. + +Optional properties: + +- brcm,irq-can-wake: if present, this means the L2 controller can be used as a + wakeup source for system suspend/resume. + +- brcm,int-fwd-mask: if present, a 32-bits bit mask to configure for the + interrupts which have a mux gate, typically UARTs. Setting these bits will + make their respective interrupts outputs bypass this 2nd level interrupt + controller completely, it completely transparent for the interrupt controller + parent + +Example: + +irq0_intc: interrupt-controller@f0406800 { + compatible = "brcm,bcm7120-l2-intc"; + interrupt-parent = <&intc>; + #interrupt-cells = <1>; + reg = <0xf0406800 0x8>; + interrupt-controller; + interrupts = <0x0 0x42 0x0>, <0x0 0x40 0x0>; + brcm,int-map-mask = <0xeb8>, <0x140>; + brcm,int-fwd-mask = <0x7>; +}; diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt index 1f8b0c507c26..c73acd060093 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt @@ -2,7 +2,13 @@ DT bindings for the R-/SH-Mobile irqpin controller Required properties: -- compatible: has to be "renesas,intc-irqpin" +- compatible: has to be "renesas,intc-irqpin-<soctype>", "renesas,intc-irqpin" + as fallback. + Examples with soctypes are: + - "renesas,intc-irqpin-r8a7740" (R-Mobile A1) + - "renesas,intc-irqpin-r8a7778" (R-Car M1A) + - "renesas,intc-irqpin-r8a7779" (R-Car H1) + - "renesas,intc-irqpin-sh73a0" (SH-Mobile AG5) - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in interrupts.txt in this directory diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt new file mode 100644 index 000000000000..1a88e62228e5 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt @@ -0,0 +1,32 @@ +DT bindings for the R-Mobile/R-Car interrupt controller + +Required properties: + +- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback. + Examples with soctypes are: + - "renesas,irqc-r8a73a4" (R-Mobile AP6) + - "renesas,irqc-r8a7790" (R-Car H2) + - "renesas,irqc-r8a7791" (R-Car M2-W) + - "renesas,irqc-r8a7792" (R-Car V2H) + - "renesas,irqc-r8a7793" (R-Car M2-N) + - "renesas,irqc-r8a7794" (R-Car E2) +- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in + interrupts.txt in this directory + +Optional properties: + +- any properties, listed in interrupts.txt, and any standard resource allocation + properties + +Example: + + irqc0: interrupt-controller@e61c0000 { + compatible = "renesas,irqc-r8a7790", "renesas,irqc"; + #interrupt-cells = <2>; + interrupt-controller; + reg = <0 0xe61c0000 0 0x200>; + interrupts = <0 0 IRQ_TYPE_LEVEL_HIGH>, + <0 1 IRQ_TYPE_LEVEL_HIGH>, + <0 2 IRQ_TYPE_LEVEL_HIGH>, + <0 3 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt new file mode 100644 index 000000000000..d9bb106bdd16 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt @@ -0,0 +1,36 @@ +Keystone 2 IRQ controller IP + +On Keystone SOCs, DSP cores can send interrupts to ARM +host using the IRQ controller IP. It provides 28 IRQ signals to ARM. +The IRQ handler running on HOST OS can identify DSP signal source by +analyzing SRCCx bits in IPCARx registers. This is one of the component +used by the IPC mechanism used on Keystone SOCs. + +Required Properties: +- compatible: should be "ti,keystone-irq" +- ti,syscon-dev : phandle and offset pair. The phandle to syscon used to + access device control registers and the offset inside + device control registers range. +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode interrupt + source should be 1. +- interrupts: interrupt reference to primary interrupt controller + +Please refer to interrupts.txt in this directory for details of the common +Interrupt Controllers bindings used by client devices. + +Example: + kirq0: keystone_irq0@026202a0 { + compatible = "ti,keystone-irq"; + ti,syscon-dev = <&devctrl 0x2a0>; + interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + dsp0: dsp0 { + compatible = "linux,rproc-user"; + ... + interrupt-parent = <&kirq0>; + interrupts = <10 2>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 9e315a44ae0c..0b23084070c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5045,6 +5045,7 @@ L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core T: git git://git.infradead.org/users/jcooper/linux.git irqchip/core +F: Documentation/devicetree/bindings/interrupt-controller/ F: drivers/irqchip/ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 82dfdeac3595..d9d32de9628c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -24,6 +24,7 @@ config ARM select GENERIC_SMP_IDLE_THREAD select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER + select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_ARCH_AUDITSYSCALL if (AEABI && !OABI_COMPAT) select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 88de943eebd6..7c81ec428b9b 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -65,24 +65,7 @@ int arch_show_interrupts(struct seq_file *p, int prec) */ void handle_IRQ(unsigned int irq, struct pt_regs *regs) { - struct pt_regs *old_regs = set_irq_regs(regs); - - irq_enter(); - - /* - * Some hardware gives randomly wrong interrupts. Rather - * than crashing, do something sensible. - */ - if (unlikely(irq >= nr_irqs)) { - if (printk_ratelimit()) - printk(KERN_WARNING "Bad IRQ%u\n", irq); - ack_bad_irq(irq); - } else { - generic_handle_irq(irq); - } - - irq_exit(); - set_irq_regs(old_regs); + __handle_domain_irq(NULL, irq, false, regs); } /* diff --git a/arch/arm/mach-imx/avic.c b/arch/arm/mach-imx/avic.c index 24b103c67f82..1a8932335b21 100644 --- a/arch/arm/mach-imx/avic.c +++ b/arch/arm/mach-imx/avic.c @@ -144,7 +144,7 @@ static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs) if (nivector == 0xffff) break; - handle_IRQ(irq_find_mapping(domain, nivector), regs); + handle_domain_irq(domain, nivector, regs); } while (1); } diff --git a/arch/arm/mach-imx/tzic.c b/arch/arm/mach-imx/tzic.c index 1d4f384ca773..4de65eeda1eb 100644 --- a/arch/arm/mach-imx/tzic.c +++ b/arch/arm/mach-imx/tzic.c @@ -141,8 +141,7 @@ static void __exception_irq_entry tzic_handle_irq(struct pt_regs *regs) while (stat) { handled = 1; irqofs = fls(stat) - 1; - handle_IRQ(irq_find_mapping(domain, - irqofs + i * 32), regs); + handle_domain_irq(domain, irqofs + i * 32, regs); stat &= ~(1 << irqofs); } } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index d0543d90db8d..9746dc24a117 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -30,6 +30,7 @@ config ARM64 select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL + select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_JUMP_LABEL diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h index 0be67821f9ce..e8a3268a891c 100644 --- a/arch/arm64/include/asm/hardirq.h +++ b/arch/arm64/include/asm/hardirq.h @@ -47,8 +47,6 @@ static inline void ack_bad_irq(unsigned int irq) irq_err_count++; } -extern void handle_IRQ(unsigned int, struct pt_regs *); - /* * No arch-specific IRQ flags. */ diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index dfa6e3e74fdd..071a6ec13bd8 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -40,33 +40,6 @@ int arch_show_interrupts(struct seq_file *p, int prec) return 0; } -/* - * handle_IRQ handles all hardware IRQ's. Decoded IRQs should - * not come via this function. Instead, they should provide their - * own 'handler'. Used by platform code implementing C-based 1st - * level decoding. - */ -void handle_IRQ(unsigned int irq, struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - - irq_enter(); - - /* - * Some hardware gives randomly wrong interrupts. Rather - * than crashing, do something sensible. - */ - if (unlikely(irq >= nr_irqs)) { - pr_warn_ratelimited("Bad IRQ%u\n", irq); - ack_bad_irq(irq); - } else { - generic_handle_irq(irq); - } - - irq_exit(); - set_irq_regs(old_regs); -} - void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { if (handle_arch_irq) diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 88e83368bbf5..e5a693b16da2 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -8,6 +8,7 @@ config OPENRISC select OF select OF_EARLY_FLATTREE select IRQ_DOMAIN + select HANDLE_DOMAIN_IRQ select HAVE_MEMBLOCK select ARCH_REQUIRE_GPIOLIB select HAVE_ARCH_TRACEHOOK diff --git a/arch/openrisc/include/asm/irq.h b/arch/openrisc/include/asm/irq.h index b84634cc95eb..d9eee0a2b7b4 100644 --- a/arch/openrisc/include/asm/irq.h +++ b/arch/openrisc/include/asm/irq.h @@ -24,7 +24,6 @@ #define NO_IRQ (-1) -void handle_IRQ(unsigned int, struct pt_regs *); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); #endif /* __ASM_OPENRISC_IRQ_H__ */ diff --git a/arch/openrisc/kernel/irq.c b/arch/openrisc/kernel/irq.c index 967eb1430203..35e478a93116 100644 --- a/arch/openrisc/kernel/irq.c +++ b/arch/openrisc/kernel/irq.c @@ -48,18 +48,6 @@ void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) handle_arch_irq = handle_irq; } -void handle_IRQ(unsigned int irq, struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - - irq_enter(); - - generic_handle_irq(irq); - - irq_exit(); - set_irq_regs(old_regs); -} - void __irq_entry do_IRQ(struct pt_regs *regs) { handle_arch_irq(regs); 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_generi |