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 | ||
183 | void nlm_setup_pic_irq(int node, int picirq, int irq, int irt) | |
184 | { | |
185 | struct nlm_pic_irq *pic_data; | |
186 | int xirq; | |
187 | ||
188 | xirq = nlm_irq_to_xirq(node, irq); | |
189 | pic_data = kzalloc(sizeof(*pic_data), GFP_KERNEL); | |
190 | BUG_ON(pic_data == NULL); | |
191 | pic_data->irt = irt; | |
192 | pic_data->picirq = picirq; | |
193 | pic_data->node = nlm_get_node(node); | |
194 | irq_set_chip_and_handler(xirq, &xlp_pic, handle_level_irq); | |
195 | irq_set_handler_data(xirq, pic_data); | |
196 | } | |
0c965407 | 197 | |
38541742 J |
198 | void nlm_set_pic_extra_ack(int node, int irq, void (*xack)(struct irq_data *)) |
199 | { | |
200 | struct nlm_pic_irq *pic_data; | |
201 | int xirq; | |
202 | ||
203 | xirq = nlm_irq_to_xirq(node, irq); | |
204 | pic_data = irq_get_handler_data(xirq); | |
205 | pic_data->extra_ack = xack; | |
206 | } | |
207 | ||
208 | static void nlm_init_node_irqs(int node) | |
209 | { | |
210 | int i, irt; | |
211 | uint64_t irqmask; | |
212 | struct nlm_soc_info *nodep; | |
213 | ||
214 | pr_info("Init IRQ for node %d\n", node); | |
215 | nodep = nlm_get_node(node); | |
216 | irqmask = PERCPU_IRQ_MASK; | |
217 | for (i = PIC_IRT_FIRST_IRQ; i <= PIC_IRT_LAST_IRQ; i++) { | |
218 | irt = nlm_irq_to_irt(i); | |
0c965407 J |
219 | if (irt == -1) |
220 | continue; | |
38541742 J |
221 | nlm_setup_pic_irq(node, i, i, irt); |
222 | /* set interrupts to first cpu in node */ | |
223 | nlm_pic_init_irt(nodep->picbase, irt, i, | |
4e45e542 | 224 | node * NLM_CPUS_PER_NODE, 0); |
38541742 | 225 | irqmask |= (1ull << i); |
0c965407 | 226 | } |
77ae798f | 227 | nodep->irqmask = irqmask; |
0c965407 J |
228 | } |
229 | ||
38541742 | 230 | void nlm_smp_irq_init(int hwcpuid) |
0c965407 | 231 | { |
38541742 J |
232 | int node, cpu; |
233 | ||
234 | node = hwcpuid / NLM_CPUS_PER_NODE; | |
235 | cpu = hwcpuid % NLM_CPUS_PER_NODE; | |
236 | ||
237 | if (cpu == 0 && node != 0) | |
238 | nlm_init_node_irqs(node); | |
77ae798f | 239 | write_c0_eimr(nlm_current_node()->irqmask); |
0c965407 J |
240 | } |
241 | ||
242 | asmlinkage void plat_irq_dispatch(void) | |
243 | { | |
244 | uint64_t eirr; | |
77ae798f | 245 | int i, node; |
0c965407 | 246 | |
77ae798f | 247 | node = nlm_nodeid(); |
220d9122 | 248 | eirr = read_c0_eirr_and_eimr(); |
8ecd0837 | 249 | if (eirr == 0) |
0c965407 J |
250 | return; |
251 | ||
8ecd0837 | 252 | i = __ffs64(eirr); |
38541742 | 253 | /* per-CPU IRQs don't need translation */ |
8ecd0837 | 254 | if (i < PIC_IRQ_BASE) { |
38541742 J |
255 | do_IRQ(i); |
256 | return; | |
257 | } | |
258 | ||
259 | /* top level irq handling */ | |
77ae798f | 260 | do_IRQ(nlm_irq_to_xirq(node, i)); |
0c965407 | 261 | } |
628f0650 J |
262 | |
263 | #ifdef CONFIG_OF | |
264 | static struct irq_domain *xlp_pic_domain; | |
265 | ||
266 | static const struct irq_domain_ops xlp_pic_irq_domain_ops = { | |
267 | .xlate = irq_domain_xlate_onetwocell, | |
268 | }; | |
269 | ||
270 | static int __init xlp_of_pic_init(struct device_node *node, | |
271 | struct device_node *parent) | |
272 | { | |
273 | const int n_picirqs = PIC_IRT_LAST_IRQ - PIC_IRQ_BASE + 1; | |
274 | struct resource res; | |
275 | int socid, ret; | |
276 | ||
277 | /* we need a hack to get the PIC's SoC chip id */ | |
278 | ret = of_address_to_resource(node, 0, &res); | |
279 | if (ret < 0) { | |
280 | pr_err("PIC %s: reg property not found!\n", node->name); | |
281 | return -EINVAL; | |
282 | } | |
283 | socid = (res.start >> 18) & 0x3; | |
284 | xlp_pic_domain = irq_domain_add_legacy(node, n_picirqs, | |
285 | nlm_irq_to_xirq(socid, PIC_IRQ_BASE), PIC_IRQ_BASE, | |
286 | &xlp_pic_irq_domain_ops, NULL); | |
287 | if (xlp_pic_domain == NULL) { | |
288 | pr_err("PIC %s: Creating legacy domain failed!\n", node->name); | |
289 | return -EINVAL; | |
290 | } | |
291 | pr_info("Node %d: IRQ domain created for PIC@%pa\n", socid, | |
292 | &res.start); | |
293 | return 0; | |
294 | } | |
295 | ||
296 | static struct of_device_id __initdata xlp_pic_irq_ids[] = { | |
297 | { .compatible = "netlogic,xlp-pic", .data = xlp_of_pic_init }, | |
298 | {}, | |
299 | }; | |
300 | #endif | |
301 | ||
302 | void __init arch_init_irq(void) | |
303 | { | |
304 | /* Initialize the irq descriptors */ | |
305 | nlm_init_percpu_irqs(); | |
306 | nlm_init_node_irqs(0); | |
307 | write_c0_eimr(nlm_current_node()->irqmask); | |
308 | #if defined(CONFIG_CPU_XLR) | |
309 | nlm_setup_fmn_irq(); | |
310 | #endif | |
311 | #if defined(CONFIG_OF) | |
312 | of_irq_init(xlp_pic_irq_ids); | |
313 | #endif | |
314 | } |