diff options
Diffstat (limited to 'arch/powerpc/platforms/52xx/mpc52xx_pic.c')
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_pic.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c new file mode 100644 index 000000000000..cd91a6c3aafa --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Programmable Interrupt Controller functions for the Freescale MPC52xx. | ||
4 | * | ||
5 | * Copyright (C) 2006 bplan GmbH | ||
6 | * | ||
7 | * Based on the code from the 2.4 kernel by | ||
8 | * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg. | ||
9 | * | ||
10 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
11 | * Copyright (C) 2003 Montavista Software, Inc | ||
12 | * | ||
13 | * This file is licensed under the terms of the GNU General Public License | ||
14 | * version 2. This program is licensed "as is" without any warranty of any | ||
15 | * kind, whether express or implied. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #undef DEBUG | ||
20 | |||
21 | #include <linux/stddef.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/signal.h> | ||
25 | #include <linux/stddef.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/hardirq.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/processor.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/prom.h> | ||
35 | #include <asm/mpc52xx.h> | ||
36 | #include "mpc52xx_pic.h" | ||
37 | |||
38 | /* | ||
39 | * | ||
40 | */ | ||
41 | |||
42 | static struct mpc52xx_intr __iomem *intr; | ||
43 | static struct mpc52xx_sdma __iomem *sdma; | ||
44 | static struct irq_host *mpc52xx_irqhost = NULL; | ||
45 | |||
46 | static unsigned char mpc52xx_map_senses[4] = { | ||
47 | IRQ_TYPE_LEVEL_HIGH, | ||
48 | IRQ_TYPE_EDGE_RISING, | ||
49 | IRQ_TYPE_EDGE_FALLING, | ||
50 | IRQ_TYPE_LEVEL_LOW, | ||
51 | }; | ||
52 | |||
53 | /* | ||
54 | * | ||
55 | */ | ||
56 | |||
57 | static inline void io_be_setbit(u32 __iomem *addr, int bitno) | ||
58 | { | ||
59 | out_be32(addr, in_be32(addr) | (1 << bitno)); | ||
60 | } | ||
61 | |||
62 | static inline void io_be_clrbit(u32 __iomem *addr, int bitno) | ||
63 | { | ||
64 | out_be32(addr, in_be32(addr) & ~(1 << bitno)); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * IRQ[0-3] interrupt irq_chip | ||
69 | */ | ||
70 | |||
71 | static void mpc52xx_extirq_mask(unsigned int virq) | ||
72 | { | ||
73 | int irq; | ||
74 | int l2irq; | ||
75 | |||
76 | irq = irq_map[virq].hwirq; | ||
77 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
78 | |||
79 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
80 | |||
81 | io_be_clrbit(&intr->ctrl, 11 - l2irq); | ||
82 | } | ||
83 | |||
84 | static void mpc52xx_extirq_unmask(unsigned int virq) | ||
85 | { | ||
86 | int irq; | ||
87 | int l2irq; | ||
88 | |||
89 | irq = irq_map[virq].hwirq; | ||
90 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
91 | |||
92 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
93 | |||
94 | io_be_setbit(&intr->ctrl, 11 - l2irq); | ||
95 | } | ||
96 | |||
97 | static void mpc52xx_extirq_ack(unsigned int virq) | ||
98 | { | ||
99 | int irq; | ||
100 | int l2irq; | ||
101 | |||
102 | irq = irq_map[virq].hwirq; | ||
103 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
104 | |||
105 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
106 | |||
107 | io_be_setbit(&intr->ctrl, 27-l2irq); | ||
108 | } | ||
109 | |||
110 | static struct irq_chip mpc52xx_extirq_irqchip = { | ||
111 | .typename = " MPC52xx IRQ[0-3] ", | ||
112 | .mask = mpc52xx_extirq_mask, | ||
113 | .unmask = mpc52xx_extirq_unmask, | ||
114 | .ack = mpc52xx_extirq_ack, | ||
115 | }; | ||
116 | |||
117 | /* | ||
118 | * Main interrupt irq_chip | ||
119 | */ | ||
120 | |||
121 | static void mpc52xx_main_mask(unsigned int virq) | ||
122 | { | ||
123 | int irq; | ||
124 | int l2irq; | ||
125 | |||
126 | irq = irq_map[virq].hwirq; | ||
127 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
128 | |||
129 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
130 | |||
131 | io_be_setbit(&intr->main_mask, 15 - l2irq); | ||
132 | } | ||
133 | |||
134 | static void mpc52xx_main_unmask(unsigned int virq) | ||
135 | { | ||
136 | int irq; | ||
137 | int l2irq; | ||
138 | |||
139 | irq = irq_map[virq].hwirq; | ||
140 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
141 | |||
142 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
143 | |||
144 | io_be_clrbit(&intr->main_mask, 15 - l2irq); | ||
145 | } | ||
146 | |||
147 | static struct irq_chip mpc52xx_main_irqchip = { | ||
148 | .typename = "MPC52xx Main", | ||
149 | .mask = mpc52xx_main_mask, | ||
150 | .mask_ack = mpc52xx_main_mask, | ||
151 | .unmask = mpc52xx_main_unmask, | ||
152 | }; | ||
153 | |||
154 | /* | ||
155 | * Peripherals interrupt irq_chip | ||
156 | */ | ||
157 | |||
158 | static void mpc52xx_periph_mask(unsigned int virq) | ||
159 | { | ||
160 | int irq; | ||
161 | int l2irq; | ||
162 | |||
163 | irq = irq_map[virq].hwirq; | ||
164 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
165 | |||
166 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
167 | |||
168 | io_be_setbit(&intr->per_mask, 31 - l2irq); | ||
169 | } | ||
170 | |||
171 | static void mpc52xx_periph_unmask(unsigned int virq) | ||
172 | { | ||
173 | int irq; | ||
174 | int l2irq; | ||
175 | |||
176 | irq = irq_map[virq].hwirq; | ||
177 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
178 | |||
179 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
180 | |||
181 | io_be_clrbit(&intr->per_mask, 31 - l2irq); | ||
182 | } | ||
183 | |||
184 | static struct irq_chip mpc52xx_periph_irqchip = { | ||
185 | .typename = "MPC52xx Peripherals", | ||
186 | .mask = mpc52xx_periph_mask, | ||
187 | .mask_ack = mpc52xx_periph_mask, | ||
188 | .unmask = mpc52xx_periph_unmask, | ||
189 | }; | ||
190 | |||
191 | /* | ||
192 | * SDMA interrupt irq_chip | ||
193 | */ | ||
194 | |||
195 | static void mpc52xx_sdma_mask(unsigned int virq) | ||
196 | { | ||
197 | int irq; | ||
198 | int l2irq; | ||
199 | |||
200 | irq = irq_map[virq].hwirq; | ||
201 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
202 | |||
203 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
204 | |||
205 | io_be_setbit(&sdma->IntMask, l2irq); | ||
206 | } | ||
207 | |||
208 | static void mpc52xx_sdma_unmask(unsigned int virq) | ||
209 | { | ||
210 | int irq; | ||
211 | int l2irq; | ||
212 | |||
213 | irq = irq_map[virq].hwirq; | ||
214 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
215 | |||
216 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
217 | |||
218 | io_be_clrbit(&sdma->IntMask, l2irq); | ||
219 | } | ||
220 | |||
221 | static void mpc52xx_sdma_ack(unsigned int virq) | ||
222 | { | ||
223 | int irq; | ||
224 | int l2irq; | ||
225 | |||
226 | irq = irq_map[virq].hwirq; | ||
227 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
228 | |||
229 | pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq); | ||
230 | |||
231 | out_be32(&sdma->IntPend, 1 << l2irq); | ||
232 | } | ||
233 | |||
234 | static struct irq_chip mpc52xx_sdma_irqchip = { | ||
235 | .typename = "MPC52xx SDMA", | ||
236 | .mask = mpc52xx_sdma_mask, | ||
237 | .unmask = mpc52xx_sdma_unmask, | ||
238 | .ack = mpc52xx_sdma_ack, | ||
239 | }; | ||
240 | |||
241 | /* | ||
242 | * irq_host | ||
243 | */ | ||
244 | |||
245 | static int mpc52xx_irqhost_match(struct irq_host *h, struct device_node *node) | ||
246 | { | ||
247 | pr_debug("%s: node=%p\n", __func__, node); | ||
248 | return mpc52xx_irqhost->host_data == node; | ||
249 | } | ||
250 | |||
251 | static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, | ||
252 | u32 * intspec, unsigned int intsize, | ||
253 | irq_hw_number_t * out_hwirq, | ||
254 | unsigned int *out_flags) | ||
255 | { | ||
256 | int intrvect_l1; | ||
257 | int intrvect_l2; | ||
258 | int intrvect_type; | ||
259 | int intrvect_linux; | ||
260 | |||
261 | if (intsize != 3) | ||
262 | return -1; | ||
263 | |||
264 | intrvect_l1 = (int)intspec[0]; | ||
265 | intrvect_l2 = (int)intspec[1]; | ||
266 | intrvect_type = (int)intspec[2]; | ||
267 | |||
268 | intrvect_linux = | ||
269 | (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) & MPC52xx_IRQ_L1_MASK; | ||
270 | intrvect_linux |= | ||
271 | (intrvect_l2 << MPC52xx_IRQ_L2_OFFSET) & MPC52xx_IRQ_L2_MASK; | ||
272 | |||
273 | pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1, | ||
274 | intrvect_l2); | ||
275 | |||
276 | *out_hwirq = intrvect_linux; | ||
277 | *out_flags = mpc52xx_map_senses[intrvect_type]; | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * this function retrieves the correct IRQ type out | ||
284 | * of the MPC regs | ||
285 | * Only externals IRQs needs this | ||
286 | */ | ||
287 | static int mpc52xx_irqx_gettype(int irq) | ||
288 | { | ||
289 | int type; | ||
290 | u32 ctrl_reg; | ||
291 | |||
292 | ctrl_reg = in_be32(&intr->ctrl); | ||
293 | type = (ctrl_reg >> (22 - irq * 2)) & 0x3; | ||
294 | |||
295 | return mpc52xx_map_senses[type]; | ||
296 | } | ||
297 | |||
298 | static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, | ||
299 | irq_hw_number_t irq) | ||
300 | { | ||
301 | int l1irq; | ||
302 | int l2irq; | ||
303 | struct irq_chip *good_irqchip; | ||
304 | void *good_handle; | ||
305 | int type; | ||
306 | |||
307 | l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET; | ||
308 | l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; | ||
309 | |||
310 | /* | ||
311 | * Most of ours IRQs will be level low | ||
312 | * Only external IRQs on some platform may be others | ||
313 | */ | ||
314 | type = IRQ_TYPE_LEVEL_LOW; | ||
315 | |||
316 | switch (l1irq) { | ||
317 | case MPC52xx_IRQ_L1_CRIT: | ||
318 | pr_debug("%s: Critical. l2=%x\n", __func__, l2irq); | ||
319 | |||
320 | BUG_ON(l2irq != 0); | ||
321 | |||
322 | type = mpc52xx_irqx_gettype(l2irq); | ||
323 | good_irqchip = &mpc52xx_extirq_irqchip; | ||
324 | break; | ||
325 | |||
326 | case MPC52xx_IRQ_L1_MAIN: | ||
327 | pr_debug("%s: Main IRQ[1-3] l2=%x\n", __func__, l2irq); | ||
328 | |||
329 | if ((l2irq >= 1) && (l2irq <= 3)) { | ||
330 | type = mpc52xx_irqx_gettype(l2irq); | ||
331 | good_irqchip = &mpc52xx_extirq_irqchip; | ||
332 | } else { | ||
333 | good_irqchip = &mpc52xx_main_irqchip; | ||
334 | } | ||
335 | break; | ||
336 | |||
337 | case MPC52xx_IRQ_L1_PERP: | ||
338 | pr_debug("%s: Peripherals. l2=%x\n", __func__, l2irq); | ||
339 | good_irqchip = &mpc52xx_periph_irqchip; | ||
340 | break; | ||
341 | |||
342 | case MPC52xx_IRQ_L1_SDMA: | ||
343 | pr_debug("%s: SDMA. l2=%x\n", __func__, l2irq); | ||
344 | good_irqchip = &mpc52xx_sdma_irqchip; | ||
345 | break; | ||
346 | |||
347 | default: | ||
348 | pr_debug("%s: Error, unknown L1 IRQ (0x%x)\n", __func__, l1irq); | ||
349 | printk(KERN_ERR "Unknow IRQ!\n"); | ||
350 | return -EINVAL; | ||
351 | } | ||
352 | |||
353 | switch (type) { | ||
354 | case IRQ_TYPE_EDGE_FALLING: | ||
355 | case IRQ_TYPE_EDGE_RISING: | ||
356 | good_handle = handle_edge_irq; | ||
357 | break; | ||
358 | default: | ||
359 | good_handle = handle_level_irq; | ||
360 | } | ||
361 | |||
362 | set_irq_chip_and_handler(virq, good_irqchip, good_handle); | ||
363 | |||
364 | pr_debug("%s: virq=%x, hw=%x. type=%x\n", __func__, virq, | ||
365 | (int)irq, type); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static struct irq_host_ops mpc52xx_irqhost_ops = { | ||
371 | .match = mpc52xx_irqhost_match, | ||
372 | .xlate = mpc52xx_irqhost_xlate, | ||
373 | .map = mpc52xx_irqhost_map, | ||
374 | }; | ||
375 | |||
376 | /* | ||
377 | * init (public) | ||
378 | */ | ||
379 | |||
380 | void __init mpc52xx_init_irq(void) | ||
381 | { | ||
382 | u32 intr_ctrl; | ||
383 | struct device_node *picnode; | ||
384 | |||
385 | /* Remap the necessary zones */ | ||
386 | picnode = of_find_compatible_node(NULL, NULL, "mpc52xx-pic"); | ||
387 | |||
388 | intr = mpc52xx_find_and_map("mpc52xx-pic"); | ||
389 | if (!intr) | ||
390 | panic(__FILE__ ": find_and_map failed on 'mpc52xx-pic'. " | ||
391 | "Check node !"); | ||
392 | |||
393 | sdma = mpc52xx_find_and_map("mpc52xx-bestcomm"); | ||
394 | if (!sdma) | ||
395 | panic(__FILE__ ": find_and_map failed on 'mpc52xx-bestcomm'. " | ||
396 | "Check node !"); | ||
397 | |||
398 | /* Disable all interrupt sources. */ | ||
399 | out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ | ||
400 | out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ | ||
401 | out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ | ||
402 | out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ | ||
403 | intr_ctrl = in_be32(&intr->ctrl); | ||
404 | intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ | ||
405 | intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ | ||
406 | 0x00001000 | /* MEE master external enable */ | ||
407 | 0x00000000 | /* 0 means disable IRQ 0-3 */ | ||
408 | 0x00000001; /* CEb route critical normally */ | ||
409 | out_be32(&intr->ctrl, intr_ctrl); | ||
410 | |||
411 | /* Zero a bunch of the priority settings. */ | ||
412 | out_be32(&intr->per_pri1, 0); | ||
413 | out_be32(&intr->per_pri2, 0); | ||
414 | out_be32(&intr->per_pri3, 0); | ||
415 | out_be32(&intr->main_pri1, 0); | ||
416 | out_be32(&intr->main_pri2, 0); | ||
417 | |||
418 | /* | ||
419 | * As last step, add an irq host to translate the real | ||
420 | * hw irq information provided by the ofw to linux virq | ||
421 | */ | ||
422 | |||
423 | mpc52xx_irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, | ||
424 | MPC52xx_IRQ_HIGHTESTHWIRQ, | ||
425 | &mpc52xx_irqhost_ops, -1); | ||
426 | |||
427 | if (!mpc52xx_irqhost) | ||
428 | panic(__FILE__ ": Cannot allocate the IRQ host\n"); | ||
429 | |||
430 | mpc52xx_irqhost->host_data = picnode; | ||
431 | printk(KERN_INFO "MPC52xx PIC is up and running!\n"); | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * get_irq (public) | ||
436 | */ | ||
437 | unsigned int mpc52xx_get_irq(void) | ||
438 | { | ||
439 | u32 status; | ||
440 | int irq = NO_IRQ_IGNORE; | ||
441 | |||
442 | status = in_be32(&intr->enc_status); | ||
443 | if (status & 0x00000400) { /* critical */ | ||
444 | irq = (status >> 8) & 0x3; | ||
445 | if (irq == 2) /* high priority peripheral */ | ||
446 | goto peripheral; | ||
447 | irq |= (MPC52xx_IRQ_L1_CRIT << MPC52xx_IRQ_L1_OFFSET) & | ||
448 | MPC52xx_IRQ_L1_MASK; | ||
449 | } else if (status & 0x00200000) { /* main */ | ||
450 | irq = (status >> 16) & 0x1f; | ||
451 | if (irq == 4) /* low priority peripheral */ | ||
452 | goto peripheral; | ||
453 | irq |= (MPC52xx_IRQ_L1_MAIN << MPC52xx_IRQ_L1_OFFSET) & | ||
454 | MPC52xx_IRQ_L1_MASK; | ||
455 | } else if (status & 0x20000000) { /* peripheral */ | ||
456 | peripheral: | ||
457 | irq = (status >> 24) & 0x1f; | ||
458 | if (irq == 0) { /* bestcomm */ | ||
459 | status = in_be32(&sdma->IntPend); | ||
460 | irq = ffs(status) - 1; | ||
461 | irq |= (MPC52xx_IRQ_L1_SDMA << MPC52xx_IRQ_L1_OFFSET) & | ||
462 | MPC52xx_IRQ_L1_MASK; | ||
463 | } else { | ||
464 | irq |= (MPC52xx_IRQ_L1_PERP << MPC52xx_IRQ_L1_OFFSET) & | ||
465 | MPC52xx_IRQ_L1_MASK; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | pr_debug("%s: irq=%x. virq=%d\n", __func__, irq, | ||
470 | irq_linear_revmap(mpc52xx_irqhost, irq)); | ||
471 | |||
472 | return irq_linear_revmap(mpc52xx_irqhost, irq); | ||
473 | } | ||