]>
Commit | Line | Data |
---|---|---|
0c965407 J |
1 | /* |
2 | * Copyright 2003-2011 NetLogic Microsystems, Inc. (NetLogic). All rights | |
3 | * reserved. | |
4 | * | |
5 | * This software is available to you under a choice of one of two | |
6 | * licenses. You may choose to be licensed under the terms of the GNU | |
7 | * General Public License (GPL) Version 2, available from the file | |
8 | * COPYING in the main directory of this source tree, or the NetLogic | |
9 | * license below: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without | |
12 | * modification, are permitted provided that the following conditions | |
13 | * are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in | |
19 | * the documentation and/or other materials provided with the | |
20 | * distribution. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY NETLOGIC ``AS IS'' AND ANY EXPRESS OR | |
23 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
24 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
29 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
30 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | |
32 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <linux/kernel.h> | |
36 | #include <linux/init.h> | |
37 | #include <linux/linkage.h> | |
38 | #include <linux/interrupt.h> | |
0c965407 J |
39 | #include <linux/mm.h> |
40 | #include <linux/slab.h> | |
41 | #include <linux/irq.h> | |
42 | ||
628f0650 J |
43 | #include <linux/irqdomain.h> |
44 | #include <linux/of_address.h> | |
45 | #include <linux/of_irq.h> | |
46 | ||
0c965407 J |
47 | #include <asm/errno.h> |
48 | #include <asm/signal.h> | |
0c965407 J |
49 | #include <asm/ptrace.h> |
50 | #include <asm/mipsregs.h> | |
51 | #include <asm/thread_info.h> | |
52 | ||
53 | #include <asm/netlogic/mips-extns.h> | |
54 | #include <asm/netlogic/interrupt.h> | |
55 | #include <asm/netlogic/haldefs.h> | |
56 | #include <asm/netlogic/common.h> | |
57 | ||
65040e22 J |
58 | #if defined(CONFIG_CPU_XLP) |
59 | #include <asm/netlogic/xlp-hal/iomap.h> | |
60 | #include <asm/netlogic/xlp-hal/xlp.h> | |
61 | #include <asm/netlogic/xlp-hal/pic.h> | |
62 | #elif defined(CONFIG_CPU_XLR) | |
0c965407 J |
63 | #include <asm/netlogic/xlr/iomap.h> |
64 | #include <asm/netlogic/xlr/pic.h> | |
ed21cfe2 | 65 | #include <asm/netlogic/xlr/fmn.h> |
65040e22 J |
66 | #else |
67 | #error "Unknown CPU" | |
68 | #endif | |
0c965407 | 69 | |
38541742 J |
70 | #ifdef CONFIG_SMP |
71 | #define SMP_IRQ_MASK ((1ULL << IRQ_IPI_SMP_FUNCTION) | \ | |
72 | (1ULL << IRQ_IPI_SMP_RESCHEDULE)) | |
73 | #else | |
74 | #define SMP_IRQ_MASK 0 | |
75 | #endif | |
70342287 | 76 | #define PERCPU_IRQ_MASK (SMP_IRQ_MASK | (1ull << IRQ_TIMER) | \ |
ed21cfe2 | 77 | (1ull << IRQ_FMN)) |
38541742 J |
78 | |
79 | struct nlm_pic_irq { | |
80 | void (*extra_ack)(struct irq_data *); | |
81 | struct nlm_soc_info *node; | |
82 | int picirq; | |
83 | int irt; | |
84 | int flags; | |
85 | }; | |
86 | ||
0c965407 J |
87 | static void xlp_pic_enable(struct irq_data *d) |
88 | { | |
89 | unsigned long flags; | |
38541742 | 90 | struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); |
0c965407 | 91 | |
38541742 J |
92 | BUG_ON(!pd); |
93 | spin_lock_irqsave(&pd->node->piclock, flags); | |
94 | nlm_pic_enable_irt(pd->node->picbase, pd->irt); | |
95 | spin_unlock_irqrestore(&pd->node->piclock, flags); | |
0c965407 J |
96 | } |
97 | ||
98 | static void xlp_pic_disable(struct irq_data *d) | |
99 | { | |
38541742 | 100 | struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); |
0c965407 | 101 | unsigned long flags; |
0c965407 | 102 | |
38541742 J |
103 | BUG_ON(!pd); |
104 | spin_lock_irqsave(&pd->node->piclock, flags); | |
105 | nlm_pic_disable_irt(pd->node->picbase, pd->irt); | |
106 | spin_unlock_irqrestore(&pd->node->piclock, flags); | |
0c965407 J |
107 | } |
108 | ||
109 | static void xlp_pic_mask_ack(struct irq_data *d) | |
110 | { | |
38541742 | 111 | struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); |
0c965407 | 112 | |
220d9122 J |
113 | clear_c0_eimr(pd->picirq); |
114 | ack_c0_eirr(pd->picirq); | |
0c965407 J |
115 | } |
116 | ||
117 | static void xlp_pic_unmask(struct irq_data *d) | |
118 | { | |
38541742 | 119 | struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); |
0c965407 | 120 | |
220d9122 | 121 | BUG_ON(!pd); |
0c965407 | 122 | |
38541742 J |
123 | if (pd->extra_ack) |
124 | pd->extra_ack(d); | |
125 | ||
220d9122 J |
126 | /* re-enable the intr on this cpu */ |
127 | set_c0_eimr(pd->picirq); | |
128 | ||
0c965407 | 129 | /* Ack is a single write, no need to lock */ |
38541742 | 130 | nlm_pic_ack(pd->node->picbase, pd->irt); |
0c965407 J |
131 | } |
132 | ||
133 | static struct irq_chip xlp_pic = { | |
134 | .name = "XLP-PIC", | |
135 | .irq_enable = xlp_pic_enable, | |
136 | .irq_disable = xlp_pic_disable, | |
137 | .irq_mask_ack = xlp_pic_mask_ack, | |
138 | .irq_unmask = xlp_pic_unmask, | |
139 | }; | |
140 | ||
141 | static void cpuintr_disable(struct irq_data *d) | |
142 | { | |
220d9122 | 143 | clear_c0_eimr(d->irq); |
0c965407 J |
144 | } |
145 | ||
146 | static void cpuintr_enable(struct irq_data *d) | |
147 | { | |
220d9122 | 148 | set_c0_eimr(d->irq); |
0c965407 J |
149 | } |
150 | ||
151 | static void cpuintr_ack(struct irq_data *d) | |
152 | { | |
220d9122 | 153 | ack_c0_eirr(d->irq); |
0c965407 J |
154 | } |
155 | ||
156 | /* | |
157 | * Chip definition for CPU originated interrupts(timer, msg) and | |
158 | * IPIs | |
159 | */ | |
160 | struct irq_chip nlm_cpu_intr = { | |
161 | .name = "XLP-CPU-INTR", | |
162 | .irq_enable = cpuintr_enable, | |
163 | .irq_disable = cpuintr_disable, | |
220d9122 J |
164 | .irq_mask = cpuintr_disable, |
165 | .irq_ack = cpuintr_ack, | |
166 | .irq_eoi = cpuintr_enable, | |
0c965407 J |
167 | }; |
168 | ||
38541742 | 169 | static void __init nlm_init_percpu_irqs(void) |
0c965407 | 170 | { |
38541742 | 171 | int i; |
0c965407 J |
172 | |
173 | for (i = 0; i < PIC_IRT_FIRST_IRQ; i++) | |
174 | irq_set_chip_and_handler(i, &nlm_cpu_intr, handle_percpu_irq); | |
0c965407 J |
175 | #ifdef CONFIG_SMP |
176 | irq_set_chip_and_handler(IRQ_IPI_SMP_FUNCTION, &nlm_cpu_intr, | |
177 | nlm_smp_function_ipi_handler); | |
178 | irq_set_chip_and_handler(IRQ_IPI_SMP_RESCHEDULE, &nlm_cpu_intr, | |
179 | nlm_smp_resched_ipi_handler); | |
0c965407 | 180 | #endif |
38541742 J |
181 | } |
182 | ||
c24a8a7a | 183 | |
38541742 J |
184 | void nlm_setup_pic_irq(int node, int picirq, int irq, int irt) |
185 | { | |
186 | struct nlm_pic_irq *pic_data; | |
187 | int xirq; | |
188 | ||
189 | xirq = nlm_irq_to_xirq(node, irq); | |
190 | pic_data = kzalloc(sizeof(*pic_data), GFP_KERNEL); | |
191 | BUG_ON(pic_data == NULL); | |
192 | pic_data->irt = irt; | |
193 | pic_data->picirq = picirq; | |
194 | pic_data->node = nlm_get_node(node); | |
195 | irq_set_chip_and_handler(xirq, &xlp_pic, handle_level_irq); | |
196 | irq_set_handler_data(xirq, pic_data); | |
197 | } | |
0c965407 | 198 | |
38541742 J |
199 | void nlm_set_pic_extra_ack(int node, int irq, void (*xack)(struct irq_data *)) |
200 | { | |
201 | struct nlm_pic_irq *pic_data; | |
202 | int xirq; | |
203 | ||
204 | xirq = nlm_irq_to_xirq(node, irq); | |
205 | pic_data = irq_get_handler_data(xirq); | |
9de10ffb J |
206 | if (WARN_ON(!pic_data)) |
207 | return; | |
38541742 J |
208 | pic_data->extra_ack = xack; |
209 | } | |
210 | ||
211 | static void nlm_init_node_irqs(int node) | |
212 | { | |
38541742 | 213 | struct nlm_soc_info *nodep; |
c24a8a7a | 214 | int i, irt; |
38541742 J |
215 | |
216 | pr_info("Init IRQ for node %d\n", node); | |
217 | nodep = nlm_get_node(node); | |
c24a8a7a | 218 | nodep->irqmask = PERCPU_IRQ_MASK; |
38541742 J |
219 | for (i = PIC_IRT_FIRST_IRQ; i <= PIC_IRT_LAST_IRQ; i++) { |
220 | irt = nlm_irq_to_irt(i); | |
c24a8a7a | 221 | if (irt == -1) /* unused irq */ |
0c965407 | 222 | continue; |
c24a8a7a J |
223 | nodep->irqmask |= 1ull << i; |
224 | if (irt == -2) /* not a direct PIC irq */ | |
225 | continue; | |
226 | ||
38541742 | 227 | nlm_pic_init_irt(nodep->picbase, irt, i, |
98d4884c | 228 | node * nlm_threads_per_node(), 0); |
c24a8a7a | 229 | nlm_setup_pic_irq(node, i, i, irt); |
0c965407 | 230 | } |
0c965407 J |
231 | } |
232 | ||
38541742 | 233 | void nlm_smp_irq_init(int hwcpuid) |
0c965407 | 234 | { |
38541742 J |
235 | int node, cpu; |
236 | ||
98d4884c J |
237 | node = nlm_cpuid_to_node(hwcpuid); |
238 | cpu = hwcpuid % nlm_threads_per_node(); | |
38541742 J |
239 | |
240 | if (cpu == 0 && node != 0) | |
241 | nlm_init_node_irqs(node); | |
77ae798f | 242 | write_c0_eimr(nlm_current_node()->irqmask); |
0c965407 J |
243 | } |
244 | ||
245 | asmlinkage void plat_irq_dispatch(void) | |
246 | { | |
247 | uint64_t eirr; | |
77ae798f | 248 | int i, node; |
0c965407 | 249 | |
77ae798f | 250 | node = nlm_nodeid(); |
220d9122 | 251 | eirr = read_c0_eirr_and_eimr(); |
8ecd0837 | 252 | if (eirr == 0) |
0c965407 J |
253 | return; |
254 | ||
8ecd0837 | 255 | i = __ffs64(eirr); |
38541742 | 256 | /* per-CPU IRQs don't need translation */ |
8ecd0837 | 257 | if (i < PIC_IRQ_BASE) { |
38541742 J |
258 | do_IRQ(i); |
259 | return; | |
260 | } | |
261 | ||
c24a8a7a J |
262 | #if defined(CONFIG_PCI_MSI) && defined(CONFIG_CPU_XLP) |
263 | /* PCI interrupts need a second level dispatch for MSI bits */ | |
264 | if (i >= PIC_PCIE_LINK_MSI_IRQ(0) && i <= PIC_PCIE_LINK_MSI_IRQ(3)) { | |
265 | nlm_dispatch_msi(node, i); | |
266 | return; | |
267 | } | |
268 | if (i >= PIC_PCIE_MSIX_IRQ(0) && i <= PIC_PCIE_MSIX_IRQ(3)) { | |
269 | nlm_dispatch_msix(node, i); | |
270 | return; | |
271 | } | |
272 | ||
273 | #endif | |
38541742 | 274 | /* top level irq handling */ |
77ae798f | 275 | do_IRQ(nlm_irq_to_xirq(node, i)); |
0c965407 | 276 | } |
628f0650 J |
277 | |
278 | #ifdef CONFIG_OF | |
628f0650 J |
279 | static const struct irq_domain_ops xlp_pic_irq_domain_ops = { |
280 | .xlate = irq_domain_xlate_onetwocell, | |
281 | }; | |
282 | ||
283 | static int __init xlp_of_pic_init(struct device_node *node, | |
284 | struct device_node *parent) | |
285 | { | |
286 | const int n_picirqs = PIC_IRT_LAST_IRQ - PIC_IRQ_BASE + 1; | |
5fcbdf7c | 287 | struct irq_domain *xlp_pic_domain; |
628f0650 | 288 | struct resource res; |
5fcbdf7c | 289 | int socid, ret, bus; |
628f0650 J |
290 | |
291 | /* we need a hack to get the PIC's SoC chip id */ | |
292 | ret = of_address_to_resource(node, 0, &res); | |
293 | if (ret < 0) { | |
294 | pr_err("PIC %s: reg property not found!\n", node->name); | |
295 | return -EINVAL; | |
296 | } | |
5fcbdf7c J |
297 | |
298 | if (cpu_is_xlp9xx()) { | |
299 | bus = (res.start >> 20) & 0xf; | |
300 | for (socid = 0; socid < NLM_NR_NODES; socid++) { | |
301 | if (!nlm_node_present(socid)) | |
302 | continue; | |
303 | if (nlm_get_node(socid)->socbus == bus) | |
304 | break; | |
305 | } | |
306 | if (socid == NLM_NR_NODES) { | |
307 | pr_err("PIC %s: Node mapping for bus %d not found!\n", | |
308 | node->name, bus); | |
309 | return -EINVAL; | |
310 | } | |
311 | } else { | |
312 | socid = (res.start >> 18) & 0x3; | |
313 | if (!nlm_node_present(socid)) { | |
314 | pr_err("PIC %s: node %d does not exist!\n", | |
315 | node->name, socid); | |
316 | return -EINVAL; | |
317 | } | |
318 | } | |
319 | ||
320 | if (!nlm_node_present(socid)) { | |
321 | pr_err("PIC %s: node %d does not exist!\n", node->name, socid); | |
322 | return -EINVAL; | |
323 | } | |
324 | ||
628f0650 J |
325 | xlp_pic_domain = irq_domain_add_legacy(node, n_picirqs, |
326 | nlm_irq_to_xirq(socid, PIC_IRQ_BASE), PIC_IRQ_BASE, | |
327 | &xlp_pic_irq_domain_ops, NULL); | |
328 | if (xlp_pic_domain == NULL) { | |
329 | pr_err("PIC %s: Creating legacy domain failed!\n", node->name); | |
330 | return -EINVAL; | |
331 | } | |
5fcbdf7c | 332 | pr_info("Node %d: IRQ domain created for PIC@%pR\n", socid, &res); |
628f0650 J |
333 | return 0; |
334 | } | |
335 | ||
336 | static struct of_device_id __initdata xlp_pic_irq_ids[] = { | |
337 | { .compatible = "netlogic,xlp-pic", .data = xlp_of_pic_init }, | |
338 | {}, | |
339 | }; | |
340 | #endif | |
341 | ||
342 | void __init arch_init_irq(void) | |
343 | { | |
344 | /* Initialize the irq descriptors */ | |
345 | nlm_init_percpu_irqs(); | |
346 | nlm_init_node_irqs(0); | |
347 | write_c0_eimr(nlm_current_node()->irqmask); | |
348 | #if defined(CONFIG_CPU_XLR) | |
349 | nlm_setup_fmn_irq(); | |
350 | #endif | |
351 | #if defined(CONFIG_OF) | |
352 | of_irq_init(xlp_pic_irq_ids); | |
353 | #endif | |
354 | } |