]> git.ipfire.org Git - people/ms/u-boot.git/blame - cpu/ppc4xx/interrupts.c
Patch by Travis Sawyer, 30 Dec 2003:
[people/ms/u-boot.git] / cpu / ppc4xx / interrupts.c
CommitLineData
f780aa2a
WD
1/*
2 * (C) Copyright 2000-2002
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
5 * (C) Copyright 2002 (440 port)
6 * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com
7 *
ba56f625
WD
8 * (C) Copyright 2003 (440GX port)
9 * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com
10 *
f780aa2a
WD
11 * See file CREDITS for list of people who contributed to this
12 * project.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of
17 * the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27 * MA 02111-1307 USA
28 */
29
30#include <common.h>
31#include <watchdog.h>
32#include <command.h>
f780aa2a
WD
33#include <asm/processor.h>
34#include <ppc4xx.h>
35#include <ppc_asm.tmpl>
36#include <commproc.h>
37#include "vecnum.h"
38
39/****************************************************************************/
40
f780aa2a
WD
41/*
42 * CPM interrupt vector functions.
43 */
44struct irq_action {
45 interrupt_handler_t *handler;
46 void *arg;
47 int count;
48};
49
50static struct irq_action irq_vecs[32];
51
52#if defined(CONFIG_440)
53static struct irq_action irq_vecs1[32]; /* For UIC1 */
54
55void uic1_interrupt( void * parms); /* UIC1 handler */
ba56f625
WD
56
57#if defined(CONFIG_440_GX)
58static struct irq_action irq_vecs2[32]; /* For UIC2 */
59
60void uic0_interrupt( void * parms); /* UIC0 handler */
61void uic2_interrupt( void * parms); /* UIC2 handler */
62#endif /* CONFIG_440_GX */
63
64#endif /* CONFIG_440 */
f780aa2a
WD
65
66/****************************************************************************/
f780aa2a
WD
67#if defined(CONFIG_440)
68
69/* SPRN changed in 440 */
70static __inline__ void set_evpr(unsigned long val)
71{
72 asm volatile("mtspr 0x03f,%0" : : "r" (val));
73}
74
75#else /* !defined(CONFIG_440) */
76
f780aa2a
WD
77static __inline__ void set_pit(unsigned long val)
78{
79 asm volatile("mtpit %0" : : "r" (val));
80}
81
82
83static __inline__ void set_tcr(unsigned long val)
84{
85 asm volatile("mttcr %0" : : "r" (val));
86}
87
88
89static __inline__ void set_evpr(unsigned long val)
90{
91 asm volatile("mtevpr %0" : : "r" (val));
92}
93#endif /* defined(CONFIG_440 */
94
f780aa2a
WD
95/****************************************************************************/
96
a8c7c708 97int interrupt_init_cpu (unsigned *decrementer_count)
f780aa2a
WD
98{
99 DECLARE_GLOBAL_DATA_PTR;
100
101 int vec;
102 unsigned long val;
103
a8c7c708
WD
104 /* decrementer is automatically reloaded */
105 *decrementer_count = 0;
d4ca31c4 106
f780aa2a
WD
107 /*
108 * Mark all irqs as free
109 */
110 for (vec=0; vec<32; vec++) {
111 irq_vecs[vec].handler = NULL;
112 irq_vecs[vec].arg = NULL;
113 irq_vecs[vec].count = 0;
114#if defined(CONFIG_440)
115 irq_vecs1[vec].handler = NULL;
116 irq_vecs1[vec].arg = NULL;
117 irq_vecs1[vec].count = 0;
ba56f625
WD
118#if defined(CONFIG_440_GX)
119 irq_vecs2[vec].handler = NULL;
120 irq_vecs2[vec].arg = NULL;
121 irq_vecs2[vec].count = 0;
122#endif /* CONFIG_440_GX */
f780aa2a
WD
123#endif
124 }
125
126#ifdef CONFIG_4xx
127 /*
128 * Init PIT
129 */
130#if defined(CONFIG_440)
131 val = mfspr( tcr );
132 val &= (~0x04400000); /* clear DIS & ARE */
133 mtspr( tcr, val );
134 mtspr( dec, 0 ); /* Prevent exception after TSR clear*/
135 mtspr( decar, 0 ); /* clear reload */
136 mtspr( tsr, 0x08000000 ); /* clear DEC status */
137 val = gd->bd->bi_intfreq/100; /* 10 msec */
138 mtspr( decar, val ); /* Set auto-reload value */
139 mtspr( dec, val ); /* Set inital val */
140#else
141 set_pit(gd->bd->bi_intfreq / 1000);
142#endif
143#endif /* CONFIG_4xx */
144
145#ifdef CONFIG_ADCIOP
146 /*
147 * Init PIT
148 */
149 set_pit(66000);
150#endif
151
152 /*
153 * Enable PIT
154 */
155 val = mfspr(tcr);
156 val |= 0x04400000;
157 mtspr(tcr, val);
158
159 /*
160 * Set EVPR to 0
161 */
162 set_evpr(0x00000000);
163
164#if defined(CONFIG_440)
ba56f625 165#if !defined(CONFIG_440_GX)
f780aa2a
WD
166 /* Install the UIC1 handlers */
167 irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0);
168 irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0);
169#endif
ba56f625
WD
170#endif
171
172#if defined(CONFIG_440_GX)
173 /* Enable UIC interrupts via UIC Base Enable Register */
174 mtdcr(uicb0er, UICB0_ALL);
175 mtdcr(uicb0cr, UICB0_ALL);
176#endif
f780aa2a
WD
177
178 return (0);
179}
180
181/****************************************************************************/
182
183/*
184 * Handle external interrupts
185 */
ba56f625
WD
186#if defined(CONFIG_440_GX)
187void external_interrupt(struct pt_regs *regs)
188{
189 ulong uic_msr;
190
191 /*
192 * Read masked interrupt status register to determine interrupt source
193 */
194 /* 440 GX uses base uic register */
195 uic_msr = mfdcr(uicb0msr);
196
197 uic0_interrupt(0);
198 uic1_interrupt(0);
199 uic2_interrupt(0);
200
201 mtdcr(uicb0sr, UICB0_ALL);
202
203 return;
204
205} /* external_interrupt CONFIG_440_GX */
206
207#else
208
f780aa2a
WD
209void external_interrupt(struct pt_regs *regs)
210{
211 ulong uic_msr;
212 ulong msr_shift;
213 int vec;
214
215 /*
216 * Read masked interrupt status register to determine interrupt source
217 */
218 uic_msr = mfdcr(uicmsr);
219 msr_shift = uic_msr;
220 vec = 0;
221
222 while (msr_shift != 0) {
223 if (msr_shift & 0x80000000) {
224 /*
225 * Increment irq counter (for debug purpose only)
226 */
227 irq_vecs[vec].count++;
228
229 if (irq_vecs[vec].handler != NULL) {
230 /* call isr */
231 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
232 } else {
233 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
234 printf ("Masking bogus interrupt vector 0x%x\n", vec);
235 }
236
237 /*
238 * After servicing the interrupt, we have to remove the status indicator.
239 */
240 mtdcr(uicsr, (0x80000000 >> vec));
241 }
242
243 /*
244 * Shift msr to next position and increment vector
245 */
246 msr_shift <<= 1;
247 vec++;
248 }
249}
ba56f625
WD
250#endif
251
252#if defined(CONFIG_440_GX)
253/* Handler for UIC0 interrupt */
254void uic0_interrupt( void * parms)
255{
256 ulong uic_msr;
257 ulong msr_shift;
258 int vec;
259
260 /*
261 * Read masked interrupt status register to determine interrupt source
262 */
263 uic_msr = mfdcr(uicmsr);
264 msr_shift = uic_msr;
265 vec = 0;
266
267 while (msr_shift != 0) {
268 if (msr_shift & 0x80000000) {
269 /*
270 * Increment irq counter (for debug purpose only)
271 */
272 irq_vecs[vec].count++;
273
274 if (irq_vecs[vec].handler != NULL) {
275 /* call isr */
276 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
277 } else {
278 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
279 printf ("Masking bogus interrupt vector (uic0) 0x%x\n", vec);
280 }
281
282 /*
283 * After servicing the interrupt, we have to remove the status indicator.
284 */
285 mtdcr(uicsr, (0x80000000 >> vec));
286 }
287
288 /*
289 * Shift msr to next position and increment vector
290 */
291 msr_shift <<= 1;
292 vec++;
293 }
294}
295
296#endif /* CONFIG_440_GX */
f780aa2a
WD
297
298#if defined(CONFIG_440)
299/* Handler for UIC1 interrupt */
300void uic1_interrupt( void * parms)
301{
302 ulong uic1_msr;
303 ulong msr_shift;
304 int vec;
305
306 /*
307 * Read masked interrupt status register to determine interrupt source
308 */
309 uic1_msr = mfdcr(uic1msr);
310 msr_shift = uic1_msr;
311 vec = 0;
312
313 while (msr_shift != 0) {
314 if (msr_shift & 0x80000000) {
315 /*
316 * Increment irq counter (for debug purpose only)
317 */
318 irq_vecs1[vec].count++;
319
320 if (irq_vecs1[vec].handler != NULL) {
321 /* call isr */
322 (*irq_vecs1[vec].handler)(irq_vecs1[vec].arg);
323 } else {
324 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> vec));
325 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
326 }
327
328 /*
329 * After servicing the interrupt, we have to remove the status indicator.
330 */
331 mtdcr(uic1sr, (0x80000000 >> vec));
332 }
333
334 /*
335 * Shift msr to next position and increment vector
336 */
337 msr_shift <<= 1;
338 vec++;
339 }
340}
341#endif /* defined(CONFIG_440) */
342
ba56f625
WD
343#if defined(CONFIG_440_GX)
344/* Handler for UIC1 interrupt */
345void uic2_interrupt( void * parms)
346{
347 ulong uic2_msr;
348 ulong msr_shift;
349 int vec;
350
351 /*
352 * Read masked interrupt status register to determine interrupt source
353 */
354 uic2_msr = mfdcr(uic2msr);
355 msr_shift = uic2_msr;
356 vec = 0;
357
358 while (msr_shift != 0) {
359 if (msr_shift & 0x80000000) {
360 /*
361 * Increment irq counter (for debug purpose only)
362 */
363 irq_vecs2[vec].count++;
364
365 if (irq_vecs2[vec].handler != NULL) {
366 /* call isr */
367 (*irq_vecs2[vec].handler)(irq_vecs2[vec].arg);
368 } else {
369 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> vec));
370 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
371 }
372
373 /*
374 * After servicing the interrupt, we have to remove the status indicator.
375 */
376 mtdcr(uic2sr, (0x80000000 >> vec));
377 }
378
379 /*
380 * Shift msr to next position and increment vector
381 */
382 msr_shift <<= 1;
383 vec++;
384 }
385}
386#endif /* defined(CONFIG_440_GX) */
387
f780aa2a
WD
388/****************************************************************************/
389
390/*
391 * Install and free a interrupt handler.
392 */
393
ba56f625 394void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg)
f780aa2a
WD
395{
396 struct irq_action *irqa = irq_vecs;
ba56f625 397 int i = vec;
f780aa2a
WD
398
399#if defined(CONFIG_440)
ba56f625
WD
400#if defined(CONFIG_440_GX)
401 if ((vec > 31) && (vec < 64)) {
402 i = vec - 32;
403 irqa = irq_vecs1;
404 } else if (vec > 63) {
405 i = vec - 64;
406 irqa = irq_vecs2;
407 }
408#else /* CONFIG_440_GX */
f780aa2a
WD
409 if (vec > 31) {
410 i = vec - 32;
411 irqa = irq_vecs1;
412 }
ba56f625
WD
413#endif /* CONFIG_440_GX */
414#endif /* CONFIG_440 */
f780aa2a
WD
415
416 if (irqa[i].handler != NULL) {
417 printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
ba56f625 418 vec, (uint) handler, (uint) irqa[i].handler);
f780aa2a
WD
419 }
420 irqa[i].handler = handler;
ba56f625 421 irqa[i].arg = arg;
f780aa2a
WD
422
423#if defined(CONFIG_440)
ba56f625
WD
424#if defined(CONFIG_440_GX)
425 if ((vec > 31) && (vec < 64))
426 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
427 else if (vec > 63)
428 mtdcr (uic2er, mfdcr (uic2er) | (0x80000000 >> i));
429 else
430#endif /* CONFIG_440_GX */
431 if (vec > 31)
432 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
f780aa2a
WD
433 else
434#endif
ba56f625 435 mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i));
f780aa2a
WD
436#if 0
437 printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
438#endif
439}
440
ba56f625 441void irq_free_handler (int vec)
f780aa2a
WD
442{
443 struct irq_action *irqa = irq_vecs;
ba56f625 444 int i = vec;
f780aa2a
WD
445
446#if defined(CONFIG_440)
ba56f625
WD
447#if defined(CONFIG_440_GX)
448 if ((vec > 31) && (vec < 64)) {
449 irqa = irq_vecs1;
450 i = vec - 32;
451 } else if (vec > 63) {
452 irqa = irq_vecs2;
453 i = vec - 64;
454 }
455#endif /* CONFIG_440_GX */
f780aa2a
WD
456 if (vec > 31) {
457 irqa = irq_vecs1;
458 i = vec - 32;
459 }
460#endif
461
462#if 0
463 printf ("Free interrupt for vector %d ==> %p\n",
464 vec, irq_vecs[vec].handler);
465#endif
466
467#if defined(CONFIG_440)
ba56f625
WD
468#if defined(CONFIG_440_GX)
469 if ((vec > 31) && (vec < 64))
470 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
471 else if (vec > 63)
472 mtdcr (uic2er, mfdcr (uic2er) & ~(0x80000000 >> i));
473 else
474#endif /* CONFIG_440_GX */
f780aa2a 475 if (vec > 31)
ba56f625 476 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
f780aa2a
WD
477 else
478#endif
ba56f625 479 mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i));
f780aa2a
WD
480
481 irqa[i].handler = NULL;
ba56f625 482 irqa[i].arg = NULL;
f780aa2a
WD
483}
484
485/****************************************************************************/
486
a8c7c708 487void timer_interrupt_cpu (struct pt_regs *regs)
f780aa2a 488{
a8c7c708
WD
489 /* nothing to do here */
490 return;
f780aa2a
WD
491}
492
493/****************************************************************************/
494
f780aa2a
WD
495#if (CONFIG_COMMANDS & CFG_CMD_IRQ)
496
497/*******************************************************************************
498 *
499 * irqinfo - print information about PCI devices
500 *
501 */
502int
503do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
504{
505 int vec;
506
507 printf ("\nInterrupt-Information:\n");
508#if defined(CONFIG_440)
509 printf ("\nUIC 0\n");
510#endif
511 printf ("Nr Routine Arg Count\n");
512
513 for (vec=0; vec<32; vec++) {
514 if (irq_vecs[vec].handler != NULL) {
515 printf ("%02d %08lx %08lx %d\n",
516 vec,
517 (ulong)irq_vecs[vec].handler,
518 (ulong)irq_vecs[vec].arg,
519 irq_vecs[vec].count);
520 }
521 }
522
523#if defined(CONFIG_440)
524 printf ("\nUIC 1\n");
525 printf ("Nr Routine Arg Count\n");
526
ba56f625 527 for (vec=0; vec<32; vec++) {
f780aa2a
WD
528 if (irq_vecs1[vec].handler != NULL)
529 printf ("%02d %08lx %08lx %d\n",
530 vec+31, (ulong)irq_vecs1[vec].handler,
531 (ulong)irq_vecs1[vec].arg, irq_vecs1[vec].count);
532 }
533 printf("\n");
534#endif
f780aa2a 535
ba56f625
WD
536#if defined(CONFIG_440_GX)
537 printf ("\nUIC 2\n");
538 printf ("Nr Routine Arg Count\n");
f780aa2a 539
ba56f625
WD
540 for (vec=0; vec<32; vec++) {
541 if (irq_vecs2[vec].handler != NULL)
542 printf ("%02d %08lx %08lx %d\n",
543 vec+63, (ulong)irq_vecs2[vec].handler,
544 (ulong)irq_vecs2[vec].arg, irq_vecs2[vec].count);
545 }
546 printf("\n");
547#endif
548
549 return 0;
550}
f780aa2a 551#endif /* CONFIG_COMMANDS & CFG_CMD_IRQ */