Commit f7472655fbe70f422c08f78d107ca24a791d7b14

Authored by Jason Cooper

Merge branch 'irqchip/broadcom' into irqchip/core

Conflicts:
	drivers/irqchip/Makefile

Showing 3 changed files Inline Diff

Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt
File was created 1 Broadcom BCM7120-style Level 2 interrupt controller
2
3 This interrupt controller hardware is a second level interrupt controller that
4 is hooked to a parent interrupt controller: e.g: ARM GIC for ARM-based
5 platforms. It can be found on BCM7xxx products starting with BCM7120.
6
7 Such an interrupt controller has the following hardware design:
8
9 - outputs multiple interrupts signals towards its interrupt controller parent
10
11 - controls how some of the interrupts will be flowing, whether they will
12 directly output an interrupt signal towards the interrupt controller parent,
13 or if they will output an interrupt signal at this 2nd level interrupt
14 controller, in particular for UARTs
15
16 - not all 32-bits within the interrupt controller actually map to an interrupt
17
18 The typical hardware layout for this controller is represented below:
19
20 2nd level interrupt line Outputs for the parent controller (e.g: ARM GIC)
21
22 0 -----[ MUX ] ------------|==========> GIC interrupt 75
23 \-----------\
24 |
25 1 -----[ MUX ] --------)---|==========> GIC interrupt 76
26 \------------|
27 |
28 2 -----[ MUX ] --------)---|==========> GIC interrupt 77
29 \------------|
30 |
31 3 ---------------------|
32 4 ---------------------|
33 5 ---------------------|
34 7 ---------------------|---|===========> GIC interrupt 66
35 9 ---------------------|
36 10 --------------------|
37 11 --------------------/
38
39 6 ------------------------\
40 |===========> GIC interrupt 64
41 8 ------------------------/
42
43 12 ........................ X
44 13 ........................ X (not connected)
45 ..
46 31 ........................ X
47
48 Required properties:
49
50 - compatible: should be "brcm,bcm7120-l2-intc"
51 - reg: specifies the base physical address and size of the registers
52 - interrupt-controller: identifies the node as an interrupt controller
53 - #interrupt-cells: specifies the number of cells needed to encode an interrupt
54 source, should be 1.
55 - interrupt-parent: specifies the phandle to the parent interrupt controller
56 this one is cascaded from
57 - interrupts: specifies the interrupt line(s) in the interrupt-parent controller
58 node, valid values depend on the type of parent interrupt controller
59 - brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts
60 are wired to this 2nd level interrupt controller, and how they match their
61 respective interrupt parents. Should match exactly the number of interrupts
62 specified in the 'interrupts' property.
63
64 Optional properties:
65
66 - brcm,irq-can-wake: if present, this means the L2 controller can be used as a
67 wakeup source for system suspend/resume.
68
69 - brcm,int-fwd-mask: if present, a 32-bits bit mask to configure for the
70 interrupts which have a mux gate, typically UARTs. Setting these bits will
71 make their respective interrupts outputs bypass this 2nd level interrupt
72 controller completely, it completely transparent for the interrupt controller
73 parent
74
75 Example:
76
77 irq0_intc: interrupt-controller@f0406800 {
78 compatible = "brcm,bcm7120-l2-intc";
79 interrupt-parent = <&intc>;
80 #interrupt-cells = <1>;
81 reg = <0xf0406800 0x8>;
82 interrupt-controller;
83 interrupts = <0x0 0x42 0x0>, <0x0 0x40 0x0>;
84 brcm,int-map-mask = <0xeb8>, <0x140>;
85 brcm,int-fwd-mask = <0x7>;
86 };
87
drivers/irqchip/Makefile
1 obj-$(CONFIG_IRQCHIP) += irqchip.o 1 obj-$(CONFIG_IRQCHIP) += irqchip.o
2 2
3 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 3 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
4 obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o 4 obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
5 obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o 5 obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
6 obj-$(CONFIG_ARCH_MMP) += irq-mmp.o 6 obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
7 obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o 7 obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
8 obj-$(CONFIG_ARCH_MXS) += irq-mxs.o 8 obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
9 obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o 9 obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
10 obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o 10 obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
11 obj-$(CONFIG_METAG) += irq-metag-ext.o 11 obj-$(CONFIG_METAG) += irq-metag-ext.o
12 obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o 12 obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
13 obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o 13 obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
14 obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o 14 obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
15 obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o 15 obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
16 obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o 16 obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
17 obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o 17 obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
18 obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o 18 obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
19 obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o 19 obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
20 obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o 20 obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
21 obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o 21 obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
22 obj-$(CONFIG_ARM_NVIC) += irq-nvic.o 22 obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
23 obj-$(CONFIG_ARM_VIC) += irq-vic.o 23 obj-$(CONFIG_ARM_VIC) += irq-vic.o
24 obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o 24 obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
25 obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o 25 obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o
26 obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o 26 obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
27 obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o 27 obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
28 obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o 28 obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
29 obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o 29 obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
30 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 30 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
31 obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o 31 obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
32 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 32 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
33 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 33 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
34 obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 34 obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
35 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 35 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
36 obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 36 obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
37 obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 37 obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \
38 irq-bcm7120-l2.o
38 obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o 39 obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
39 40
drivers/irqchip/irq-bcm7120-l2.c
File was created 1 /*
2 * Broadcom BCM7120 style Level 2 interrupt controller driver
3 *
4 * Copyright (C) 2014 Broadcom Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/of.h>
18 #include <linux/of_irq.h>
19 #include <linux/of_address.h>
20 #include <linux/of_platform.h>
21 #include <linux/interrupt.h>
22 #include <linux/irq.h>
23 #include <linux/io.h>
24 #include <linux/irqdomain.h>
25 #include <linux/reboot.h>
26 #include <linux/irqchip/chained_irq.h>
27
28 #include "irqchip.h"
29
30 #include <asm/mach/irq.h>
31
32 /* Register offset in the L2 interrupt controller */
33 #define IRQEN 0x00
34 #define IRQSTAT 0x04
35
36 struct bcm7120_l2_intc_data {
37 void __iomem *base;
38 struct irq_domain *domain;
39 bool can_wake;
40 u32 irq_fwd_mask;
41 u32 irq_map_mask;
42 u32 saved_mask;
43 };
44
45 static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
46 {
47 struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
48 struct irq_chip *chip = irq_desc_get_chip(desc);
49 u32 status;
50
51 chained_irq_enter(chip, desc);
52
53 status = __raw_readl(b->base + IRQSTAT);
54
55 if (status == 0) {
56 do_bad_IRQ(irq, desc);
57 goto out;
58 }
59
60 do {
61 irq = ffs(status) - 1;
62 status &= ~(1 << irq);
63 generic_handle_irq(irq_find_mapping(b->domain, irq));
64 } while (status);
65
66 out:
67 chained_irq_exit(chip, desc);
68 }
69
70 static void bcm7120_l2_intc_suspend(struct irq_data *d)
71 {
72 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
73 struct bcm7120_l2_intc_data *b = gc->private;
74 u32 reg;
75
76 irq_gc_lock(gc);
77 /* Save the current mask and the interrupt forward mask */
78 b->saved_mask = __raw_readl(b->base) | b->irq_fwd_mask;
79 if (b->can_wake) {
80 reg = b->saved_mask | gc->wake_active;
81 __raw_writel(reg, b->base);
82 }
83 irq_gc_unlock(gc);
84 }
85
86 static void bcm7120_l2_intc_resume(struct irq_data *d)
87 {
88 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
89 struct bcm7120_l2_intc_data *b = gc->private;
90
91 /* Restore the saved mask */
92 irq_gc_lock(gc);
93 __raw_writel(b->saved_mask, b->base);
94 irq_gc_unlock(gc);
95 }
96
97 static int bcm7120_l2_intc_init_one(struct device_node *dn,
98 struct bcm7120_l2_intc_data *data,
99 int irq, const __be32 *map_mask)
100 {
101 int parent_irq;
102
103 parent_irq = irq_of_parse_and_map(dn, irq);
104 if (parent_irq < 0) {
105 pr_err("failed to map interrupt %d\n", irq);
106 return parent_irq;
107 }
108
109 data->irq_map_mask |= be32_to_cpup(map_mask + irq);
110
111 irq_set_handler_data(parent_irq, data);
112 irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
113
114 return 0;
115 }
116
117 int __init bcm7120_l2_intc_of_init(struct device_node *dn,
118 struct device_node *parent)
119 {
120 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
121 struct bcm7120_l2_intc_data *data;
122 struct irq_chip_generic *gc;
123 struct irq_chip_type *ct;
124 const __be32 *map_mask;
125 int num_parent_irqs;
126 int ret = 0, len, irq;
127
128 data = kzalloc(sizeof(*data), GFP_KERNEL);
129 if (!data)
130 return -ENOMEM;
131
132 data->base = of_iomap(dn, 0);
133 if (!data->base) {
134 pr_err("failed to remap intc L2 registers\n");
135 ret = -ENOMEM;
136 goto out_free;
137 }
138
139 if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask))
140 data->irq_fwd_mask = 0;
141
142 /* Enable all interrupt specified in the interrupt forward mask and have
143 * the other disabled
144 */
145 __raw_writel(data->irq_fwd_mask, data->base + IRQEN);
146
147 num_parent_irqs = of_irq_count(dn);
148 if (num_parent_irqs <= 0) {
149 pr_err("invalid number of parent interrupts\n");
150 ret = -ENOMEM;
151 goto out_unmap;
152 }
153
154 map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
155 if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) {
156 pr_err("invalid brcm,int-map-mask property\n");
157 ret = -EINVAL;
158 goto out_unmap;
159 }
160
161 for (irq = 0; irq < num_parent_irqs; irq++) {
162 ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask);
163 if (ret)
164 goto out_unmap;
165 }
166
167 data->domain = irq_domain_add_linear(dn, 32,
168 &irq_generic_chip_ops, NULL);
169 if (!data->domain) {
170 ret = -ENOMEM;
171 goto out_unmap;
172 }
173
174 ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
175 dn->full_name, handle_level_irq, clr, 0,
176 IRQ_GC_INIT_MASK_CACHE);
177 if (ret) {
178 pr_err("failed to allocate generic irq chip\n");
179 goto out_free_domain;
180 }
181
182 gc = irq_get_domain_generic_chip(data->domain, 0);
183 gc->unused = 0xfffffff & ~data->irq_map_mask;
184 gc->reg_base = data->base;
185 gc->private = data;
186 ct = gc->chip_types;
187
188 ct->regs.mask = IRQEN;
189 ct->chip.irq_mask = irq_gc_mask_clr_bit;
190 ct->chip.irq_unmask = irq_gc_mask_set_bit;
191 ct->chip.irq_ack = irq_gc_noop;
192 ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
193 ct->chip.irq_resume = bcm7120_l2_intc_resume;
194
195 if (of_property_read_bool(dn, "brcm,irq-can-wake")) {
196 data->can_wake = true;
197 /* This IRQ chip can wake the system, set all relevant child
198 * interupts in wake_enabled mask
199 */
200 gc->wake_enabled = 0xffffffff;
201 gc->wake_enabled &= ~gc->unused;
202 ct->chip.irq_set_wake = irq_gc_set_wake;
203 }
204
205 pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
206 data->base, num_parent_irqs);
207
208 return 0;
209
210 out_free_domain:
211 irq_domain_remove(data->domain);
212 out_unmap:
213 iounmap(data->base);
214 out_free:
215 kfree(data);
216 return ret;
217 }
218 IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc",
219 bcm7120_l2_intc_of_init);
220