diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 15:50:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 15:50:54 -0400 |
commit | bfaf245022b4b8661af2e35f467cf0e91943c24c (patch) | |
tree | b5a6ee49a047557a791eb897c8c9545a155e36b7 /drivers/irqchip | |
parent | 96d928ed75c4ba4253e82910a697ec7b06ace8b4 (diff) | |
parent | 3e20a26b02bd4f24945c87407df51948dd488620 (diff) |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle:
"This is the main pull request for MIPS for Linux 4.1. Most
noteworthy:
- Add more Octeon-optimized crypto functions
- Octeon crypto preemption and locking fixes
- Little endian support for Octeon
- Use correct CSR to soft reset Octeons
- Support LEDs on the Octeon-based DSR-1000N
- Fix PCI interrupt mapping for the Octeon-based DSR-1000N
- Mark prom_free_prom_memory() as __init for a number of systems
- Support for Imagination's Pistachio SOC. This includes arch and
CLK bits. I'd like to merge pinctrl bits later
- Improve parallelism of csum_partial for certain pipelines
- Organize DTB files in subdirs like other architectures
- Implement read_sched_clock for all MIPS platforms other than
Octeon
- Massive series of 38 fixes and cleanups for the FPU emulator /
kernel
- Further FPU remulator work to support new features. This sits on a
separate branch which also has been pulled into the 4.1 KVM branch
- Clean up and fixes for the SEAD3 eval board; remove unused file
- Various updates for Netlogic platforms
- A number of small updates for Loongson 3 platforms
- Increase the memory limit for ATH79 platforms to 256MB
- A fair number of fixes and updates for BCM47xx platforms
- Finish the implementation of XPA support
- MIPS FDC support. No, not floppy controller but Fast Debug Channel :)
- Detect the R16000 used in SGI legacy platforms
- Fix Kconfig dependencies for the SSB bus support"
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (265 commits)
MIPS: Makefile: Fix MIPS ASE detection code
MIPS: asm: elf: Set O32 default FPU flags
MIPS: BCM47XX: Fix detecting Microsoft MN-700 & Asus WL500G
MIPS: Kconfig: Disable SMP/CPS for 64-bit
MIPS: Hibernate: flush TLB entries earlier
MIPS: smp-cps: cpu_set FPU mask if FPU present
MIPS: lose_fpu(): Disable FPU when MSA enabled
MIPS: ralink: add missing symbol for RALINK_ILL_ACC
MIPS: ralink: Fix bad config symbol in PCI makefile.
SSB: fix Kconfig dependencies
MIPS: Malta: Detect and fix bad memsize values
Revert "MIPS: Avoid pipeline stalls on some MIPS32R2 cores."
MIPS: Octeon: Delete override of cpu_has_mips_r2_exec_hazard.
MIPS: Fix cpu_has_mips_r2_exec_hazard.
MIPS: kernel: entry.S: Set correct ISA level for mips_ihb
MIPS: asm: spinlock: Fix addiu instruction for R10000_LLSC_WAR case
MIPS: r4kcache: Use correct base register for MIPS R6 cache flushes
MIPS: Kconfig: Fix typo for the r2-to-r6 emulator kernel parameter
MIPS: unaligned: Fix regular load/store instruction emulation for EVA
MIPS: unaligned: Surround load/store macros in do {} while statements
...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 5 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7038-l1.c | 335 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 193 | ||||
-rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 9 | ||||
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 40 |
6 files changed, 515 insertions, 68 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index c8d260e33a90..6de62a96e79c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -60,6 +60,11 @@ config ATMEL_AIC5_IRQ | |||
60 | select MULTI_IRQ_HANDLER | 60 | select MULTI_IRQ_HANDLER |
61 | select SPARSE_IRQ | 61 | select SPARSE_IRQ |
62 | 62 | ||
63 | config BCM7038_L1_IRQ | ||
64 | bool | ||
65 | select GENERIC_IRQ_CHIP | ||
66 | select IRQ_DOMAIN | ||
67 | |||
63 | config BCM7120_L2_IRQ | 68 | config BCM7120_L2_IRQ |
64 | bool | 69 | bool |
65 | select GENERIC_IRQ_CHIP | 70 | select GENERIC_IRQ_CHIP |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 552a74027601..dda4927e47a6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -40,6 +40,7 @@ obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o | |||
40 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o | 40 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o |
41 | obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o | 41 | obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o |
42 | obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o | 42 | obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o |
43 | obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o | ||
43 | obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o | 44 | obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o |
44 | obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o | 45 | obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o |
45 | obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o | 46 | obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o |
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c new file mode 100644 index 000000000000..d3b8c8be15f6 --- /dev/null +++ b/drivers/irqchip/irq-bcm7038-l1.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * Broadcom BCM7038 style Level 1 interrupt controller driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Broadcom Corporation | ||
5 | * Author: Kevin Cernekee | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
13 | |||
14 | #include <linux/bitops.h> | ||
15 | #include <linux/kconfig.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <linux/irq.h> | ||
22 | #include <linux/irqdomain.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_irq.h> | ||
26 | #include <linux/of_address.h> | ||
27 | #include <linux/of_platform.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/smp.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/irqchip/chained_irq.h> | ||
33 | |||
34 | #include "irqchip.h" | ||
35 | |||
36 | #define IRQS_PER_WORD 32 | ||
37 | #define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4) | ||
38 | #define MAX_WORDS 8 | ||
39 | |||
40 | struct bcm7038_l1_cpu; | ||
41 | |||
42 | struct bcm7038_l1_chip { | ||
43 | raw_spinlock_t lock; | ||
44 | unsigned int n_words; | ||
45 | struct irq_domain *domain; | ||
46 | struct bcm7038_l1_cpu *cpus[NR_CPUS]; | ||
47 | u8 affinity[MAX_WORDS * IRQS_PER_WORD]; | ||
48 | }; | ||
49 | |||
50 | struct bcm7038_l1_cpu { | ||
51 | void __iomem *map_base; | ||
52 | u32 mask_cache[0]; | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * STATUS/MASK_STATUS/MASK_SET/MASK_CLEAR are packed one right after another: | ||
57 | * | ||
58 | * 7038: | ||
59 | * 0x1000_1400: W0_STATUS | ||
60 | * 0x1000_1404: W1_STATUS | ||
61 | * 0x1000_1408: W0_MASK_STATUS | ||
62 | * 0x1000_140c: W1_MASK_STATUS | ||
63 | * 0x1000_1410: W0_MASK_SET | ||
64 | * 0x1000_1414: W1_MASK_SET | ||
65 | * 0x1000_1418: W0_MASK_CLEAR | ||
66 | * 0x1000_141c: W1_MASK_CLEAR | ||
67 | * | ||
68 | * 7445: | ||
69 | * 0xf03e_1500: W0_STATUS | ||
70 | * 0xf03e_1504: W1_STATUS | ||
71 | * 0xf03e_1508: W2_STATUS | ||
72 | * 0xf03e_150c: W3_STATUS | ||
73 | * 0xf03e_1510: W4_STATUS | ||
74 | * 0xf03e_1514: W0_MASK_STATUS | ||
75 | * 0xf03e_1518: W1_MASK_STATUS | ||
76 | * [...] | ||
77 | */ | ||
78 | |||
79 | static inline unsigned int reg_status(struct bcm7038_l1_chip *intc, | ||
80 | unsigned int word) | ||
81 | { | ||
82 | return (0 * intc->n_words + word) * sizeof(u32); | ||
83 | } | ||
84 | |||
85 | static inline unsigned int reg_mask_status(struct bcm7038_l1_chip *intc, | ||
86 | unsigned int word) | ||
87 | { | ||
88 | return (1 * intc->n_words + word) * sizeof(u32); | ||
89 | } | ||
90 | |||
91 | static inline unsigned int reg_mask_set(struct bcm7038_l1_chip *intc, | ||
92 | unsigned int word) | ||
93 | { | ||
94 | return (2 * intc->n_words + word) * sizeof(u32); | ||
95 | } | ||
96 | |||
97 | static inline unsigned int reg_mask_clr(struct bcm7038_l1_chip *intc, | ||
98 | unsigned int word) | ||
99 | { | ||
100 | return (3 * intc->n_words + word) * sizeof(u32); | ||
101 | } | ||
102 | |||
103 | static inline u32 l1_readl(void __iomem *reg) | ||
104 | { | ||
105 | if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) | ||
106 | return ioread32be(reg); | ||
107 | else | ||
108 | return readl(reg); | ||
109 | } | ||
110 | |||
111 | static inline void l1_writel(u32 val, void __iomem *reg) | ||
112 | { | ||
113 | if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) | ||
114 | iowrite32be(val, reg); | ||
115 | else | ||
116 | writel(val, reg); | ||
117 | } | ||
118 | |||
119 | static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc) | ||
120 | { | ||
121 | struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc); | ||
122 | struct bcm7038_l1_cpu *cpu; | ||
123 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
124 | unsigned int idx; | ||
125 | |||
126 | #ifdef CONFIG_SMP | ||
127 | cpu = intc->cpus[cpu_logical_map(smp_processor_id())]; | ||
128 | #else | ||
129 | cpu = intc->cpus[0]; | ||
130 | #endif | ||
131 | |||
132 | chained_irq_enter(chip, desc); | ||
133 | |||
134 | for (idx = 0; idx < intc->n_words; idx++) { | ||
135 | int base = idx * IRQS_PER_WORD; | ||
136 | unsigned long pending, flags; | ||
137 | int hwirq; | ||
138 | |||
139 | raw_spin_lock_irqsave(&intc->lock, flags); | ||
140 | pending = l1_readl(cpu->map_base + reg_status(intc, idx)) & | ||
141 | ~cpu->mask_cache[idx]; | ||
142 | raw_spin_unlock_irqrestore(&intc->lock, flags); | ||
143 | |||
144 | for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { | ||
145 | generic_handle_irq(irq_find_mapping(intc->domain, | ||
146 | base + hwirq)); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | chained_irq_exit(chip, desc); | ||
151 | } | ||
152 | |||
153 | static void __bcm7038_l1_unmask(struct irq_data *d, unsigned int cpu_idx) | ||
154 | { | ||
155 | struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); | ||
156 | u32 word = d->hwirq / IRQS_PER_WORD; | ||
157 | u32 mask = BIT(d->hwirq % IRQS_PER_WORD); | ||
158 | |||
159 | intc->cpus[cpu_idx]->mask_cache[word] &= ~mask; | ||
160 | l1_writel(mask, intc->cpus[cpu_idx]->map_base + | ||
161 | reg_mask_clr(intc, word)); | ||
162 | } | ||
163 | |||
164 | static void __bcm7038_l1_mask(struct irq_data *d, unsigned int cpu_idx) | ||
165 | { | ||
166 | struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); | ||
167 | u32 word = d->hwirq / IRQS_PER_WORD; | ||
168 | u32 mask = BIT(d->hwirq % IRQS_PER_WORD); | ||
169 | |||
170 | intc->cpus[cpu_idx]->mask_cache[word] |= mask; | ||
171 | l1_writel(mask, intc->cpus[cpu_idx]->map_base + | ||
172 | reg_mask_set(intc, word)); | ||
173 | } | ||
174 | |||
175 | static void bcm7038_l1_unmask(struct irq_data *d) | ||
176 | { | ||
177 | struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); | ||
178 | unsigned long flags; | ||
179 | |||
180 | raw_spin_lock_irqsave(&intc->lock, flags); | ||
181 | __bcm7038_l1_unmask(d, intc->affinity[d->hwirq]); | ||
182 | raw_spin_unlock_irqrestore(&intc->lock, flags); | ||
183 | } | ||
184 | |||
185 | static void bcm7038_l1_mask(struct irq_data *d) | ||
186 | { | ||
187 | struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); | ||
188 | unsigned long flags; | ||
189 | |||
190 | raw_spin_lock_irqsave(&intc->lock, flags); | ||
191 | __bcm7038_l1_mask(d, intc->affinity[d->hwirq]); | ||
192 | raw_spin_unlock_irqrestore(&intc->lock, flags); | ||
193 | } | ||
194 | |||
195 | static int bcm7038_l1_set_affinity(struct irq_data *d, | ||
196 | const struct cpumask *dest, | ||
197 | bool force) | ||
198 | { | ||
199 | struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); | ||
200 | unsigned long flags; | ||
201 | irq_hw_number_t hw = d->hwirq; | ||
202 | u32 word = hw / IRQS_PER_WORD; | ||
203 | u32 mask = BIT(hw % IRQS_PER_WORD); | ||
204 | unsigned int first_cpu = cpumask_any_and(dest, cpu_online_mask); | ||
205 | bool was_disabled; | ||
206 | |||
207 | raw_spin_lock_irqsave(&intc->lock, flags); | ||
208 | |||
209 | was_disabled = !!(intc->cpus[intc->affinity[hw]]->mask_cache[word] & | ||
210 | mask); | ||
211 | __bcm7038_l1_mask(d, intc->affinity[hw]); | ||
212 | intc->affinity[hw] = first_cpu; | ||
213 | if (!was_disabled) | ||
214 | __bcm7038_l1_unmask(d, first_cpu); | ||
215 | |||
216 | raw_spin_unlock_irqrestore(&intc->lock, flags); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int __init bcm7038_l1_init_one(struct device_node *dn, | ||
221 | unsigned int idx, | ||
222 | struct bcm7038_l1_chip *intc) | ||
223 | { | ||
224 | struct resource res; | ||
225 | resource_size_t sz; | ||
226 | struct bcm7038_l1_cpu *cpu; | ||
227 | unsigned int i, n_words, parent_irq; | ||
228 | |||
229 | if (of_address_to_resource(dn, idx, &res)) | ||
230 | return -EINVAL; | ||
231 | sz = resource_size(&res); | ||
232 | n_words = sz / REG_BYTES_PER_IRQ_WORD; | ||
233 | |||
234 | if (n_words > MAX_WORDS) | ||
235 | return -EINVAL; | ||
236 | else if (!intc->n_words) | ||
237 | intc->n_words = n_words; | ||
238 | else if (intc->n_words != n_words) | ||
239 | return -EINVAL; | ||
240 | |||
241 | cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32), | ||
242 | GFP_KERNEL); | ||
243 | if (!cpu) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | cpu->map_base = ioremap(res.start, sz); | ||
247 | if (!cpu->map_base) | ||
248 | return -ENOMEM; | ||
249 | |||
250 | for (i = 0; i < n_words; i++) { | ||
251 | l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i)); | ||
252 | cpu->mask_cache[i] = 0xffffffff; | ||
253 | } | ||
254 | |||
255 | parent_irq = irq_of_parse_and_map(dn, idx); | ||
256 | if (!parent_irq) { | ||
257 | pr_err("failed to map parent interrupt %d\n", parent_irq); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | irq_set_handler_data(parent_irq, intc); | ||
261 | irq_set_chained_handler(parent_irq, bcm7038_l1_irq_handle); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static struct irq_chip bcm7038_l1_irq_chip = { | ||
267 | .name = "bcm7038-l1", | ||
268 | .irq_mask = bcm7038_l1_mask, | ||
269 | .irq_unmask = bcm7038_l1_unmask, | ||
270 | .irq_set_affinity = bcm7038_l1_set_affinity, | ||
271 | }; | ||
272 | |||
273 | static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, | ||
274 | irq_hw_number_t hw_irq) | ||
275 | { | ||
276 | irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq); | ||
277 | irq_set_chip_data(virq, d->host_data); | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static const struct irq_domain_ops bcm7038_l1_domain_ops = { | ||
282 | .xlate = irq_domain_xlate_onecell, | ||
283 | .map = bcm7038_l1_map, | ||
284 | }; | ||
285 | |||
286 | int __init bcm7038_l1_of_init(struct device_node *dn, | ||
287 | struct device_node *parent) | ||
288 | { | ||
289 | struct bcm7038_l1_chip *intc; | ||
290 | int idx, ret; | ||
291 | |||
292 | intc = kzalloc(sizeof(*intc), GFP_KERNEL); | ||
293 | if (!intc) | ||
294 | return -ENOMEM; | ||
295 | |||
296 | raw_spin_lock_init(&intc->lock); | ||
297 | for_each_possible_cpu(idx) { | ||
298 | ret = bcm7038_l1_init_one(dn, idx, intc); | ||
299 | if (ret < 0) { | ||
300 | if (idx) | ||
301 | break; | ||
302 | pr_err("failed to remap intc L1 registers\n"); | ||
303 | goto out_free; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, | ||
308 | &bcm7038_l1_domain_ops, | ||
309 | intc); | ||
310 | if (!intc->domain) { | ||
311 | ret = -ENOMEM; | ||
312 | goto out_unmap; | ||
313 | } | ||
314 | |||
315 | pr_info("registered BCM7038 L1 intc (mem: 0x%p, IRQs: %d)\n", | ||
316 | intc->cpus[0]->map_base, IRQS_PER_WORD * intc->n_words); | ||
317 | |||
318 | return 0; | ||
319 | |||
320 | out_unmap: | ||
321 | for_each_possible_cpu(idx) { | ||
322 | struct bcm7038_l1_cpu *cpu = intc->cpus[idx]; | ||
323 | |||
324 | if (cpu) { | ||
325 | if (cpu->map_base) | ||
326 | iounmap(cpu->map_base); | ||
327 | kfree(cpu); | ||
328 | } | ||
329 | } | ||
330 | out_free: | ||
331 | kfree(intc); | ||
332 | return ret; | ||
333 | } | ||
334 | |||
335 | IRQCHIP_DECLARE(bcm7038_l1, "brcm,bcm7038-l1-intc", bcm7038_l1_of_init); | ||
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 8eec8e1201d9..3ba5cc780fcb 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/kconfig.h> | 16 | #include <linux/kconfig.h> |
17 | #include <linux/kernel.h> | ||
17 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
18 | #include <linux/of.h> | 19 | #include <linux/of.h> |
19 | #include <linux/of_irq.h> | 20 | #include <linux/of_irq.h> |
@@ -34,15 +35,21 @@ | |||
34 | #define IRQSTAT 0x04 | 35 | #define IRQSTAT 0x04 |
35 | 36 | ||
36 | #define MAX_WORDS 4 | 37 | #define MAX_WORDS 4 |
38 | #define MAX_MAPPINGS (MAX_WORDS * 2) | ||
37 | #define IRQS_PER_WORD 32 | 39 | #define IRQS_PER_WORD 32 |
38 | 40 | ||
39 | struct bcm7120_l2_intc_data { | 41 | struct bcm7120_l2_intc_data { |
40 | unsigned int n_words; | 42 | unsigned int n_words; |
41 | void __iomem *base[MAX_WORDS]; | 43 | void __iomem *map_base[MAX_MAPPINGS]; |
44 | void __iomem *pair_base[MAX_WORDS]; | ||
45 | int en_offset[MAX_WORDS]; | ||
46 | int stat_offset[MAX_WORDS]; | ||
42 | struct irq_domain *domain; | 47 | struct irq_domain *domain; |
43 | bool can_wake; | 48 | bool can_wake; |
44 | u32 irq_fwd_mask[MAX_WORDS]; | 49 | u32 irq_fwd_mask[MAX_WORDS]; |
45 | u32 irq_map_mask[MAX_WORDS]; | 50 | u32 irq_map_mask[MAX_WORDS]; |
51 | int num_parent_irqs; | ||
52 | const __be32 *map_mask_prop; | ||
46 | }; | 53 | }; |
47 | 54 | ||
48 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | 55 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) |
@@ -61,7 +68,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | |||
61 | int hwirq; | 68 | int hwirq; |
62 | 69 | ||
63 | irq_gc_lock(gc); | 70 | irq_gc_lock(gc); |
64 | pending = irq_reg_readl(gc, IRQSTAT) & gc->mask_cache; | 71 | pending = irq_reg_readl(gc, b->stat_offset[idx]) & |
72 | gc->mask_cache; | ||
65 | irq_gc_unlock(gc); | 73 | irq_gc_unlock(gc); |
66 | 74 | ||
67 | for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { | 75 | for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { |
@@ -76,27 +84,30 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | |||
76 | static void bcm7120_l2_intc_suspend(struct irq_data *d) | 84 | static void bcm7120_l2_intc_suspend(struct irq_data *d) |
77 | { | 85 | { |
78 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | 86 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
87 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
79 | struct bcm7120_l2_intc_data *b = gc->private; | 88 | struct bcm7120_l2_intc_data *b = gc->private; |
80 | 89 | ||
81 | irq_gc_lock(gc); | 90 | irq_gc_lock(gc); |
82 | if (b->can_wake) | 91 | if (b->can_wake) |
83 | irq_reg_writel(gc, gc->mask_cache | gc->wake_active, IRQEN); | 92 | irq_reg_writel(gc, gc->mask_cache | gc->wake_active, |
93 | ct->regs.mask); | ||
84 | irq_gc_unlock(gc); | 94 | irq_gc_unlock(gc); |
85 | } | 95 | } |
86 | 96 | ||
87 | static void bcm7120_l2_intc_resume(struct irq_data *d) | 97 | static void bcm7120_l2_intc_resume(struct irq_data *d) |
88 | { | 98 | { |
89 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | 99 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); |
100 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
90 | 101 | ||
91 | /* Restore the saved mask */ | 102 | /* Restore the saved mask */ |
92 | irq_gc_lock(gc); | 103 | irq_gc_lock(gc); |
93 | irq_reg_writel(gc, gc->mask_cache, IRQEN); | 104 | irq_reg_writel(gc, gc->mask_cache, ct->regs.mask); |
94 | irq_gc_unlock(gc); | 105 | irq_gc_unlock(gc); |
95 | } | 106 | } |
96 | 107 | ||
97 | static int bcm7120_l2_intc_init_one(struct device_node *dn, | 108 | static int bcm7120_l2_intc_init_one(struct device_node *dn, |
98 | struct bcm7120_l2_intc_data *data, | 109 | struct bcm7120_l2_intc_data *data, |
99 | int irq, const __be32 *map_mask) | 110 | int irq) |
100 | { | 111 | { |
101 | int parent_irq; | 112 | int parent_irq; |
102 | unsigned int idx; | 113 | unsigned int idx; |
@@ -110,9 +121,15 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | |||
110 | /* For multiple parent IRQs with multiple words, this looks like: | 121 | /* For multiple parent IRQs with multiple words, this looks like: |
111 | * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> | 122 | * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> |
112 | */ | 123 | */ |
113 | for (idx = 0; idx < data->n_words; idx++) | 124 | for (idx = 0; idx < data->n_words; idx++) { |
114 | data->irq_map_mask[idx] |= | 125 | if (data->map_mask_prop) { |
115 | be32_to_cpup(map_mask + irq * data->n_words + idx); | 126 | data->irq_map_mask[idx] |= |
127 | be32_to_cpup(data->map_mask_prop + | ||
128 | irq * data->n_words + idx); | ||
129 | } else { | ||
130 | data->irq_map_mask[idx] = 0xffffffff; | ||
131 | } | ||
132 | } | ||
116 | 133 | ||
117 | irq_set_handler_data(parent_irq, data); | 134 | irq_set_handler_data(parent_irq, data); |
118 | irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); | 135 | irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); |
@@ -120,68 +137,107 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | |||
120 | return 0; | 137 | return 0; |
121 | } | 138 | } |
122 | 139 | ||
123 | int __init bcm7120_l2_intc_of_init(struct device_node *dn, | 140 | static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn, |
124 | struct device_node *parent) | 141 | struct bcm7120_l2_intc_data *data) |
142 | { | ||
143 | int ret; | ||
144 | |||
145 | data->map_base[0] = of_iomap(dn, 0); | ||
146 | if (!data->map_base[0]) { | ||
147 | pr_err("unable to map registers\n"); | ||
148 | return -ENOMEM; | ||
149 | } | ||
150 | |||
151 | data->pair_base[0] = data->map_base[0]; | ||
152 | data->en_offset[0] = IRQEN; | ||
153 | data->stat_offset[0] = IRQSTAT; | ||
154 | data->n_words = 1; | ||
155 | |||
156 | ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", | ||
157 | data->irq_fwd_mask, data->n_words); | ||
158 | if (ret != 0 && ret != -EINVAL) { | ||
159 | /* property exists but has the wrong number of words */ | ||
160 | pr_err("invalid brcm,int-fwd-mask property\n"); | ||
161 | return -EINVAL; | ||
162 | } | ||
163 | |||
164 | data->map_mask_prop = of_get_property(dn, "brcm,int-map-mask", &ret); | ||
165 | if (!data->map_mask_prop || | ||
166 | (ret != (sizeof(__be32) * data->num_parent_irqs * data->n_words))) { | ||
167 | pr_err("invalid brcm,int-map-mask property\n"); | ||
168 | return -EINVAL; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn, | ||
175 | struct bcm7120_l2_intc_data *data) | ||
176 | { | ||
177 | unsigned int gc_idx; | ||
178 | |||
179 | for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) { | ||
180 | unsigned int map_idx = gc_idx * 2; | ||
181 | void __iomem *en = of_iomap(dn, map_idx + 0); | ||
182 | void __iomem *stat = of_iomap(dn, map_idx + 1); | ||
183 | void __iomem *base = min(en, stat); | ||
184 | |||
185 | data->map_base[map_idx + 0] = en; | ||
186 | data->map_base[map_idx + 1] = stat; | ||
187 | |||
188 | if (!base) | ||
189 | break; | ||
190 | |||
191 | data->pair_base[gc_idx] = base; | ||
192 | data->en_offset[gc_idx] = en - base; | ||
193 | data->stat_offset[gc_idx] = stat - base; | ||
194 | } | ||
195 | |||
196 | if (!gc_idx) { | ||
197 | pr_err("unable to map registers\n"); | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | |||
201 | data->n_words = gc_idx; | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | int __init bcm7120_l2_intc_probe(struct device_node *dn, | ||
206 | struct device_node *parent, | ||
207 | int (*iomap_regs_fn)(struct device_node *, | ||
208 | struct bcm7120_l2_intc_data *), | ||
209 | const char *intc_name) | ||
125 | { | 210 | { |
126 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; | 211 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; |
127 | struct bcm7120_l2_intc_data *data; | 212 | struct bcm7120_l2_intc_data *data; |
128 | struct irq_chip_generic *gc; | 213 | struct irq_chip_generic *gc; |
129 | struct irq_chip_type *ct; | 214 | struct irq_chip_type *ct; |
130 | const __be32 *map_mask; | 215 | int ret = 0; |
131 | int num_parent_irqs; | ||
132 | int ret = 0, len; | ||
133 | unsigned int idx, irq, flags; | 216 | unsigned int idx, irq, flags; |
134 | 217 | ||
135 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 218 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
136 | if (!data) | 219 | if (!data) |
137 | return -ENOMEM; | 220 | return -ENOMEM; |
138 | 221 | ||
139 | for (idx = 0; idx < MAX_WORDS; idx++) { | 222 | data->num_parent_irqs = of_irq_count(dn); |
140 | data->base[idx] = of_iomap(dn, idx); | 223 | if (data->num_parent_irqs <= 0) { |
141 | if (!data->base[idx]) | ||
142 | break; | ||
143 | data->n_words = idx + 1; | ||
144 | } | ||
145 | if (!data->n_words) { | ||
146 | pr_err("failed to remap intc L2 registers\n"); | ||
147 | ret = -ENOMEM; | ||
148 | goto out_unmap; | ||
149 | } | ||
150 | |||
151 | /* Enable all interrupts specified in the interrupt forward mask; | ||
152 | * disable all others. If the property doesn't exist (-EINVAL), | ||
153 | * assume all zeroes. | ||
154 | */ | ||
155 | ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", | ||
156 | data->irq_fwd_mask, data->n_words); | ||
157 | if (ret == 0 || ret == -EINVAL) { | ||
158 | for (idx = 0; idx < data->n_words; idx++) | ||
159 | __raw_writel(data->irq_fwd_mask[idx], | ||
160 | data->base[idx] + IRQEN); | ||
161 | } else { | ||
162 | /* property exists but has the wrong number of words */ | ||
163 | pr_err("invalid int-fwd-mask property\n"); | ||
164 | ret = -EINVAL; | ||
165 | goto out_unmap; | ||
166 | } | ||
167 | |||
168 | num_parent_irqs = of_irq_count(dn); | ||
169 | if (num_parent_irqs <= 0) { | ||
170 | pr_err("invalid number of parent interrupts\n"); | 224 | pr_err("invalid number of parent interrupts\n"); |
171 | ret = -ENOMEM; | 225 | ret = -ENOMEM; |
172 | goto out_unmap; | 226 | goto out_unmap; |
173 | } | 227 | } |
174 | 228 | ||
175 | map_mask = of_get_property(dn, "brcm,int-map-mask", &len); | 229 | ret = iomap_regs_fn(dn, data); |
176 | if (!map_mask || | 230 | if (ret < 0) |
177 | (len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) { | ||
178 | pr_err("invalid brcm,int-map-mask property\n"); | ||
179 | ret = -EINVAL; | ||
180 | goto out_unmap; | 231 | goto out_unmap; |
232 | |||
233 | for (idx = 0; idx < data->n_words; idx++) { | ||
234 | __raw_writel(data->irq_fwd_mask[idx], | ||
235 | data->pair_base[idx] + | ||
236 | data->en_offset[idx]); | ||
181 | } | 237 | } |
182 | 238 | ||
183 | for (irq = 0; irq < num_parent_irqs; irq++) { | 239 | for (irq = 0; irq < data->num_parent_irqs; irq++) { |
184 | ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask); | 240 | ret = bcm7120_l2_intc_init_one(dn, data, irq); |
185 | if (ret) | 241 | if (ret) |
186 | goto out_unmap; | 242 | goto out_unmap; |
187 | } | 243 | } |
@@ -215,11 +271,12 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn, | |||
215 | gc = irq_get_domain_generic_chip(data->domain, irq); | 271 | gc = irq_get_domain_generic_chip(data->domain, irq); |
216 | 272 | ||
217 | gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; | 273 | gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; |
218 | gc->reg_base = data->base[idx]; | ||
219 | gc->private = data; | 274 | gc->private = data; |
220 | ct = gc->chip_types; | 275 | ct = gc->chip_types; |
221 | 276 | ||
222 | ct->regs.mask = IRQEN; | 277 | gc->reg_base = data->pair_base[idx]; |
278 | ct->regs.mask = data->en_offset[idx]; | ||
279 | |||
223 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | 280 | ct->chip.irq_mask = irq_gc_mask_clr_bit; |
224 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | 281 | ct->chip.irq_unmask = irq_gc_mask_set_bit; |
225 | ct->chip.irq_ack = irq_gc_noop; | 282 | ct->chip.irq_ack = irq_gc_noop; |
@@ -236,20 +293,38 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn, | |||
236 | } | 293 | } |
237 | } | 294 | } |
238 | 295 | ||
239 | pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n", | 296 | pr_info("registered %s intc (mem: 0x%p, parent IRQ(s): %d)\n", |
240 | data->base[0], num_parent_irqs); | 297 | intc_name, data->map_base[0], data->num_parent_irqs); |
241 | 298 | ||
242 | return 0; | 299 | return 0; |
243 | 300 | ||
244 | out_free_domain: | 301 | out_free_domain: |
245 | irq_domain_remove(data->domain); | 302 | irq_domain_remove(data->domain); |
246 | out_unmap: | 303 | out_unmap: |
247 | for (idx = 0; idx < MAX_WORDS; idx++) { | 304 | for (idx = 0; idx < MAX_MAPPINGS; idx++) { |
248 | if (data->base[idx]) | 305 | if (data->map_base[idx]) |
249 | iounmap(data->base[idx]); | 306 | iounmap(data->map_base[idx]); |
250 | } | 307 | } |
251 | kfree(data); | 308 | kfree(data); |
252 | return ret; | 309 | return ret; |
253 | } | 310 | } |
311 | |||
312 | int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, | ||
313 | struct device_node *parent) | ||
314 | { | ||
315 | return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120, | ||
316 | "BCM7120 L2"); | ||
317 | } | ||
318 | |||
319 | int __init bcm7120_l2_intc_probe_3380(struct device_node *dn, | ||
320 | struct device_node *parent) | ||
321 | { | ||
322 | return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380, | ||
323 | "BCM3380 L2"); | ||
324 | } | ||
325 | |||
254 | IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", | 326 | IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", |
255 | bcm7120_l2_intc_of_init); | 327 | bcm7120_l2_intc_probe_7120); |
328 | |||
329 | IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc", | ||
330 | bcm7120_l2_intc_probe_3380); | ||
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 313c2c64498a..d6bcc6be0777 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c | |||
@@ -136,7 +136,11 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, | |||
136 | 136 | ||
137 | /* Disable all interrupts by default */ | 137 | /* Disable all interrupts by default */ |
138 | writel(0xffffffff, data->base + CPU_MASK_SET); | 138 | writel(0xffffffff, data->base + CPU_MASK_SET); |
139 | writel(0xffffffff, data->base + CPU_CLEAR); | 139 | |
140 | /* Wakeup interrupts may be retained from S5 (cold boot) */ | ||
141 | data->can_wake = of_property_read_bool(np, "brcm,irq-can-wake"); | ||
142 | if (!data->can_wake) | ||
143 | writel(0xffffffff, data->base + CPU_CLEAR); | ||
140 | 144 | ||
141 | data->parent_irq = irq_of_parse_and_map(np, 0); | 145 | data->parent_irq = irq_of_parse_and_map(np, 0); |
142 | if (!data->parent_irq) { | 146 | if (!data->parent_irq) { |
@@ -188,8 +192,7 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, | |||
188 | ct->chip.irq_suspend = brcmstb_l2_intc_suspend; | 192 | ct->chip.irq_suspend = brcmstb_l2_intc_suspend; |
189 | ct->chip.irq_resume = brcmstb_l2_intc_resume; | 193 | ct->chip.irq_resume = brcmstb_l2_intc_resume; |
190 | 194 | ||
191 | if (of_property_read_bool(np, "brcm,irq-can-wake")) { | 195 | if (data->can_wake) { |
192 | data->can_wake = true; | ||
193 | /* This IRQ chip can wake the system, set all child interrupts | 196 | /* This IRQ chip can wake the system, set all child interrupts |
194 | * in wake_enabled mask | 197 | * in wake_enabled mask |
195 | */ | 198 | */ |
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index f2d269bca789..bc48b7dc89ec 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c | |||
@@ -239,7 +239,7 @@ int gic_get_c0_compare_int(void) | |||
239 | int gic_get_c0_perfcount_int(void) | 239 | int gic_get_c0_perfcount_int(void) |
240 | { | 240 | { |
241 | if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) { | 241 | if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) { |
242 | /* Is the erformance counter shared with the timer? */ | 242 | /* Is the performance counter shared with the timer? */ |
243 | if (cp0_perfcount_irq < 0) | 243 | if (cp0_perfcount_irq < 0) |
244 | return -1; | 244 | return -1; |
245 | return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; | 245 | return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; |
@@ -248,6 +248,29 @@ int gic_get_c0_perfcount_int(void) | |||
248 | GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); | 248 | GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); |
249 | } | 249 | } |
250 | 250 | ||
251 | int gic_get_c0_fdc_int(void) | ||
252 | { | ||
253 | if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) { | ||
254 | /* Is the FDC IRQ even present? */ | ||
255 | if (cp0_fdc_irq < 0) | ||
256 | return -1; | ||
257 | return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * Some cores claim the FDC is routable but it doesn't actually seem to | ||
262 | * be connected. | ||
263 | */ | ||
264 | switch (current_cpu_type()) { | ||
265 | case CPU_INTERAPTIV: | ||
266 | case CPU_PROAPTIV: | ||
267 | return -1; | ||
268 | } | ||
269 | |||
270 | return irq_create_mapping(gic_irq_domain, | ||
271 | GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC)); | ||
272 | } | ||
273 | |||
251 | static void gic_handle_shared_int(void) | 274 | static void gic_handle_shared_int(void) |
252 | { | 275 | { |
253 | unsigned int i, intr, virq; | 276 | unsigned int i, intr, virq; |
@@ -613,15 +636,20 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, | |||
613 | * of the MIPS kernel code does not use the percpu IRQ API for | 636 | * of the MIPS kernel code does not use the percpu IRQ API for |
614 | * the CP0 timer and performance counter interrupts. | 637 | * the CP0 timer and performance counter interrupts. |
615 | */ | 638 | */ |
616 | if (intr != GIC_LOCAL_INT_TIMER && intr != GIC_LOCAL_INT_PERFCTR) { | 639 | switch (intr) { |
640 | case GIC_LOCAL_INT_TIMER: | ||
641 | case GIC_LOCAL_INT_PERFCTR: | ||
642 | case GIC_LOCAL_INT_FDC: | ||
643 | irq_set_chip_and_handler(virq, | ||
644 | &gic_all_vpes_local_irq_controller, | ||
645 | handle_percpu_irq); | ||
646 | break; | ||
647 | default: | ||
617 | irq_set_chip_and_handler(virq, | 648 | irq_set_chip_and_handler(virq, |
618 | &gic_local_irq_controller, | 649 | &gic_local_irq_controller, |
619 | handle_percpu_devid_irq); | 650 | handle_percpu_devid_irq); |
620 | irq_set_percpu_devid(virq); | 651 | irq_set_percpu_devid(virq); |
621 | } else { | 652 | break; |
622 | irq_set_chip_and_handler(virq, | ||
623 | &gic_all_vpes_local_irq_controller, | ||
624 | handle_percpu_irq); | ||
625 | } | 653 | } |
626 | 654 | ||
627 | spin_lock_irqsave(&gic_lock, flags); | 655 | spin_lock_irqsave(&gic_lock, flags); |