aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2014-10-03 03:02:33 -0400
committerThomas Gleixner <tglx@linutronix.de>2014-10-03 03:02:33 -0400
commit2828c9cdb8bd30f49c48210c014ccdd4cb994931 (patch)
treea91d42306e6aca87407df53dfce0d4a9e4eb8e0e
parentdb985cbd67c45f875ef43cb5febfaa8cbd203c27 (diff)
parentfae119b6d427a087be0c6a85431c1b8ee9ffea96 (diff)
Merge tag 'irqchip-core-3.18-2' of git://git.infradead.org/users/jcooper/linux into irq/core
irqchip core changes for v3.18 (round 2) from Jason Cooper * atmel: - Add sama5d4 support - Correct # irqs for sama5d3 * broadcom: - Add bcm7120 l2 interrupt controller and DT binding * gic-v3: - Add CPU PM notifier - Add enable/disable support to gic_enable_redist
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt2
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt86
-rw-r--r--drivers/irqchip/Makefile3
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c12
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c219
-rw-r--r--drivers/irqchip/irq-gic-v3.c87
6 files changed, 385 insertions, 24 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 @@
2 2
3Required properties: 3Required properties:
4- compatible: Should be "atmel,<chip>-aic" 4- compatible: Should be "atmel,<chip>-aic"
5 <chip> can be "at91rm9200" or "sama5d3" 5 <chip> can be "at91rm9200", "sama5d3" or "sama5d4"
6- interrupt-controller: Identifies the node as an interrupt controller. 6- interrupt-controller: Identifies the node as an interrupt controller.
7- interrupt-parent: For single AIC system, it is an empty property. 7- interrupt-parent: For single AIC system, it is an empty property.
8- #interrupt-cells: The number of cells to define the interrupts. It should be 3. 8- #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 @@
1Broadcom BCM7120-style Level 2 interrupt controller
2
3This interrupt controller hardware is a second level interrupt controller that
4is hooked to a parent interrupt controller: e.g: ARM GIC for ARM-based
5platforms. It can be found on BCM7xxx products starting with BCM7120.
6
7Such 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
18The typical hardware layout for this controller is represented below:
19
202nd level interrupt line Outputs for the parent controller (e.g: ARM GIC)
21
220 -----[ MUX ] ------------|==========> GIC interrupt 75
23 \-----------\
24 |
251 -----[ MUX ] --------)---|==========> GIC interrupt 76
26 \------------|
27 |
282 -----[ MUX ] --------)---|==========> GIC interrupt 77
29 \------------|
30 |
313 ---------------------|
324 ---------------------|
335 ---------------------|
347 ---------------------|---|===========> GIC interrupt 66
359 ---------------------|
3610 --------------------|
3711 --------------------/
38
396 ------------------------\
40 |===========> GIC interrupt 64
418 ------------------------/
42
4312 ........................ X
4413 ........................ X (not connected)
45..
4631 ........................ X
47
48Required 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
64Optional 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
75Example:
76
77irq0_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};
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index fc2e4f77d3db..bec790678016 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
34obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 34obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
35obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 35obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
36obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 36obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
37obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 37obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \
38 irq-bcm7120-l2.o
38obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o 39obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index 04fe2c1b5178..a11aae8fb006 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -295,6 +295,7 @@ static void __init sama5d3_aic_irq_fixup(struct device_node *root)
295 295
296static const struct of_device_id __initdata aic5_irq_fixups[] = { 296static const struct of_device_id __initdata aic5_irq_fixups[] = {
297 { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup }, 297 { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
298 { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
298 { /* sentinel */ }, 299 { /* sentinel */ },
299}; 300};
300 301
@@ -341,7 +342,7 @@ static int __init aic5_of_init(struct device_node *node,
341 return 0; 342 return 0;
342} 343}
343 344
344#define NR_SAMA5D3_IRQS 50 345#define NR_SAMA5D3_IRQS 48
345 346
346static int __init sama5d3_aic5_of_init(struct device_node *node, 347static int __init sama5d3_aic5_of_init(struct device_node *node,
347 struct device_node *parent) 348 struct device_node *parent)
@@ -349,3 +350,12 @@ static int __init sama5d3_aic5_of_init(struct device_node *node,
349 return aic5_of_init(node, parent, NR_SAMA5D3_IRQS); 350 return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
350} 351}
351IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init); 352IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
353
354#define NR_SAMA5D4_IRQS 68
355
356static int __init sama5d4_aic5_of_init(struct device_node *node,
357 struct device_node *parent)
358{
359 return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
360}
361IRQCHIP_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 @@
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
36struct 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
45static 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
66out:
67 chained_irq_exit(chip, desc);
68}
69
70static 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
86static 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
97static 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
117int __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
210out_free_domain:
211 irq_domain_remove(data->domain);
212out_unmap:
213 iounmap(data->base);
214out_free:
215 kfree(data);
216 return ret;
217}
218IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc",
219 bcm7120_l2_intc_of_init);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 9e3144975696..7247a3cc7ba1 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18#include <linux/cpu.h> 18#include <linux/cpu.h>
19#include <linux/cpu_pm.h>
19#include <linux/delay.h> 20#include <linux/delay.h>
20#include <linux/interrupt.h> 21#include <linux/interrupt.h>
21#include <linux/of.h> 22#include <linux/of.h>
@@ -155,7 +156,7 @@ static void gic_enable_sre(void)
155 pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); 156 pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
156} 157}
157 158
158static void gic_enable_redist(void) 159static void gic_enable_redist(bool enable)
159{ 160{
160 void __iomem *rbase; 161 void __iomem *rbase;
161 u32 count = 1000000; /* 1s! */ 162 u32 count = 1000000; /* 1s! */
@@ -163,20 +164,30 @@ static void gic_enable_redist(void)
163 164
164 rbase = gic_data_rdist_rd_base(); 165 rbase = gic_data_rdist_rd_base();
165 166
166 /* Wake up this CPU redistributor */
167 val = readl_relaxed(rbase + GICR_WAKER); 167 val = readl_relaxed(rbase + GICR_WAKER);
168 val &= ~GICR_WAKER_ProcessorSleep; 168 if (enable)
169 /* Wake up this CPU redistributor */
170 val &= ~GICR_WAKER_ProcessorSleep;
171 else
172 val |= GICR_WAKER_ProcessorSleep;
169 writel_relaxed(val, rbase + GICR_WAKER); 173 writel_relaxed(val, rbase + GICR_WAKER);
170 174
171 while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) { 175 if (!enable) { /* Check that GICR_WAKER is writeable */
172 count--; 176 val = readl_relaxed(rbase + GICR_WAKER);
173 if (!count) { 177 if (!(val & GICR_WAKER_ProcessorSleep))
174 pr_err_ratelimited("redist didn't wake up...\n"); 178 return; /* No PM support in this redistributor */
175 return; 179 }
176 } 180
181 while (count--) {
182 val = readl_relaxed(rbase + GICR_WAKER);
183 if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
184 break;
177 cpu_relax(); 185 cpu_relax();
178 udelay(1); 186 udelay(1);
179 }; 187 };
188 if (!count)
189 pr_err_ratelimited("redistributor failed to %s...\n",
190 enable ? "wakeup" : "sleep");
180} 191}
181 192
182/* 193/*
@@ -372,6 +383,21 @@ static int gic_populate_rdist(void)
372 return -ENODEV; 383 return -ENODEV;
373} 384}
374 385
386static void gic_cpu_sys_reg_init(void)
387{
388 /* Enable system registers */
389 gic_enable_sre();
390
391 /* Set priority mask register */
392 gic_write_pmr(DEFAULT_PMR_VALUE);
393
394 /* EOI deactivates interrupt too (mode 0) */
395 gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
396
397 /* ... and let's hit the road... */
398 gic_write_grpen1(1);
399}
400
375static void gic_cpu_init(void) 401static void gic_cpu_init(void)
376{ 402{
377 void __iomem *rbase; 403 void __iomem *rbase;
@@ -380,23 +406,14 @@ static void gic_cpu_init(void)
380 if (gic_populate_rdist()) 406 if (gic_populate_rdist())
381 return; 407 return;
382 408
383 gic_enable_redist(); 409 gic_enable_redist(true);
384 410
385 rbase = gic_data_rdist_sgi_base(); 411 rbase = gic_data_rdist_sgi_base();
386 412
387 gic_cpu_config(rbase, gic_redist_wait_for_rwp); 413 gic_cpu_config(rbase, gic_redist_wait_for_rwp);
388 414
389 /* Enable system registers */ 415 /* initialise system registers */
390 gic_enable_sre(); 416 gic_cpu_sys_reg_init();
391
392 /* Set priority mask register */
393 gic_write_pmr(DEFAULT_PMR_VALUE);
394
395 /* EOI deactivates interrupt too (mode 0) */
396 gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
397
398 /* ... and let's hit the road... */
399 gic_write_grpen1(1);
400} 417}
401 418
402#ifdef CONFIG_SMP 419#ifdef CONFIG_SMP
@@ -532,6 +549,33 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
532#define gic_smp_init() do { } while(0) 549#define gic_smp_init() do { } while(0)
533#endif 550#endif
534 551
552#ifdef CONFIG_CPU_PM
553static int gic_cpu_pm_notifier(struct notifier_block *self,
554 unsigned long cmd, void *v)
555{
556 if (cmd == CPU_PM_EXIT) {
557 gic_enable_redist(true);
558 gic_cpu_sys_reg_init();
559 } else if (cmd == CPU_PM_ENTER) {
560 gic_write_grpen1(0);
561 gic_enable_redist(false);
562 }
563 return NOTIFY_OK;
564}
565
566static struct notifier_block gic_cpu_pm_notifier_block = {
567 .notifier_call = gic_cpu_pm_notifier,
568};
569
570static void gic_cpu_pm_init(void)
571{
572 cpu_pm_register_notifier(&gic_cpu_pm_notifier_block);
573}
574
575#else
576static inline void gic_cpu_pm_init(void) { }
577#endif /* CONFIG_CPU_PM */
578
535static struct irq_chip gic_chip = { 579static struct irq_chip gic_chip = {
536 .name = "GICv3", 580 .name = "GICv3",
537 .irq_mask = gic_mask_irq, 581 .irq_mask = gic_mask_irq,
@@ -671,6 +715,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
671 gic_smp_init(); 715 gic_smp_init();
672 gic_dist_init(); 716 gic_dist_init();
673 gic_cpu_init(); 717 gic_cpu_init();
718 gic_cpu_pm_init();
674 719
675 return 0; 720 return 0;
676 721