blob: 3aae4ce8b997e6be4b0ddd40d7c49f3db89101fc [file] [log] [blame]
wdenkf780aa22002-09-18 19:21:21 +00001/*
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 *
wdenkba56f622004-02-06 23:19:44 +00008 * (C) Copyright 2003 (440GX port)
9 * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com
10 *
wdenkf780aa22002-09-18 19:21:21 +000011 * 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>
wdenkf780aa22002-09-18 19:21:21 +000033#include <asm/processor.h>
34#include <ppc4xx.h>
35#include <ppc_asm.tmpl>
36#include <commproc.h>
37#include "vecnum.h"
38
Wolfgang Denkd87080b2006-03-31 18:32:53 +020039DECLARE_GLOBAL_DATA_PTR;
40
wdenkf780aa22002-09-18 19:21:21 +000041/****************************************************************************/
42
wdenkf780aa22002-09-18 19:21:21 +000043/*
44 * CPM interrupt vector functions.
45 */
46struct irq_action {
47 interrupt_handler_t *handler;
48 void *arg;
49 int count;
50};
51
52static struct irq_action irq_vecs[32];
53
54#if defined(CONFIG_440)
55static struct irq_action irq_vecs1[32]; /* For UIC1 */
56
57void uic1_interrupt( void * parms); /* UIC1 handler */
wdenkba56f622004-02-06 23:19:44 +000058
Stefan Roese846b0dd2005-08-08 12:42:22 +020059#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +000060static struct irq_action irq_vecs2[32]; /* For UIC2 */
61
62void uic0_interrupt( void * parms); /* UIC0 handler */
63void uic2_interrupt( void * parms); /* UIC2 handler */
Stefan Roese846b0dd2005-08-08 12:42:22 +020064#endif /* CONFIG_440GX */
wdenkba56f622004-02-06 23:19:44 +000065
66#endif /* CONFIG_440 */
wdenkf780aa22002-09-18 19:21:21 +000067
68/****************************************************************************/
wdenkf780aa22002-09-18 19:21:21 +000069#if defined(CONFIG_440)
70
71/* SPRN changed in 440 */
72static __inline__ void set_evpr(unsigned long val)
73{
74 asm volatile("mtspr 0x03f,%0" : : "r" (val));
75}
76
77#else /* !defined(CONFIG_440) */
78
wdenkf780aa22002-09-18 19:21:21 +000079static __inline__ void set_pit(unsigned long val)
80{
81 asm volatile("mtpit %0" : : "r" (val));
82}
83
84
85static __inline__ void set_tcr(unsigned long val)
86{
87 asm volatile("mttcr %0" : : "r" (val));
88}
89
90
91static __inline__ void set_evpr(unsigned long val)
92{
93 asm volatile("mtevpr %0" : : "r" (val));
94}
95#endif /* defined(CONFIG_440 */
96
wdenkf780aa22002-09-18 19:21:21 +000097/****************************************************************************/
98
wdenka8c7c702003-12-06 19:49:23 +000099int interrupt_init_cpu (unsigned *decrementer_count)
wdenkf780aa22002-09-18 19:21:21 +0000100{
wdenkf780aa22002-09-18 19:21:21 +0000101 int vec;
102 unsigned long val;
103
wdenka8c7c702003-12-06 19:49:23 +0000104 /* decrementer is automatically reloaded */
105 *decrementer_count = 0;
wdenkd4ca31c2004-01-02 14:00:00 +0000106
wdenkf780aa22002-09-18 19:21:21 +0000107 /*
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;
Stefan Roese846b0dd2005-08-08 12:42:22 +0200118#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000119 irq_vecs2[vec].handler = NULL;
120 irq_vecs2[vec].arg = NULL;
121 irq_vecs2[vec].count = 0;
Stefan Roese846b0dd2005-08-08 12:42:22 +0200122#endif /* CONFIG_440GX */
wdenkf780aa22002-09-18 19:21:21 +0000123#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 */
stroese68e02362005-04-07 05:32:44 +0000137 val = gd->bd->bi_intfreq/1000; /* 1 msec */
wdenkf780aa22002-09-18 19:21:21 +0000138 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)
Stefan Roese846b0dd2005-08-08 12:42:22 +0200165#if !defined(CONFIG_440GX)
wdenkf780aa22002-09-18 19:21:21 +0000166 /* Install the UIC1 handlers */
167 irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0);
168 irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0);
169#endif
wdenkba56f622004-02-06 23:19:44 +0000170#endif
171
Stefan Roese846b0dd2005-08-08 12:42:22 +0200172#if defined(CONFIG_440GX)
wdenkaaf224a2004-03-14 15:20:55 +0000173 /* Take the GX out of compatibility mode
174 * Travis Sawyer, 9 Mar 2004
175 * NOTE: 440gx user manual inconsistency here
176 * Compatibility mode and Ethernet Clock select are not
177 * correct in the manual
178 */
179 mfsdr(sdr_mfr, val);
180 val &= ~0x10000000;
181 mtsdr(sdr_mfr,val);
182
wdenkba56f622004-02-06 23:19:44 +0000183 /* Enable UIC interrupts via UIC Base Enable Register */
wdenkaaf224a2004-03-14 15:20:55 +0000184 mtdcr(uicb0sr, UICB0_ALL);
185 mtdcr(uicb0er, 0x54000000);
186 /* None are critical */
187 mtdcr(uicb0cr, 0);
wdenkba56f622004-02-06 23:19:44 +0000188#endif
wdenkf780aa22002-09-18 19:21:21 +0000189
190 return (0);
191}
192
193/****************************************************************************/
194
195/*
196 * Handle external interrupts
197 */
Stefan Roese846b0dd2005-08-08 12:42:22 +0200198#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000199void external_interrupt(struct pt_regs *regs)
200{
201 ulong uic_msr;
202
203 /*
204 * Read masked interrupt status register to determine interrupt source
205 */
206 /* 440 GX uses base uic register */
207 uic_msr = mfdcr(uicb0msr);
208
wdenkaaf224a2004-03-14 15:20:55 +0000209 if ( (UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr) )
210 uic0_interrupt(0);
wdenkba56f622004-02-06 23:19:44 +0000211
wdenkaaf224a2004-03-14 15:20:55 +0000212 if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
213 uic1_interrupt(0);
214
215 if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
216 uic2_interrupt(0);
217
218 mtdcr(uicb0sr, uic_msr);
wdenkba56f622004-02-06 23:19:44 +0000219
220 return;
221
Stefan Roese846b0dd2005-08-08 12:42:22 +0200222} /* external_interrupt CONFIG_440GX */
wdenkba56f622004-02-06 23:19:44 +0000223
224#else
225
wdenkf780aa22002-09-18 19:21:21 +0000226void external_interrupt(struct pt_regs *regs)
227{
228 ulong uic_msr;
229 ulong msr_shift;
230 int vec;
231
232 /*
233 * Read masked interrupt status register to determine interrupt source
234 */
235 uic_msr = mfdcr(uicmsr);
236 msr_shift = uic_msr;
237 vec = 0;
238
239 while (msr_shift != 0) {
240 if (msr_shift & 0x80000000) {
241 /*
242 * Increment irq counter (for debug purpose only)
243 */
244 irq_vecs[vec].count++;
245
246 if (irq_vecs[vec].handler != NULL) {
247 /* call isr */
248 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
249 } else {
250 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
251 printf ("Masking bogus interrupt vector 0x%x\n", vec);
252 }
253
254 /*
255 * After servicing the interrupt, we have to remove the status indicator.
256 */
257 mtdcr(uicsr, (0x80000000 >> vec));
258 }
259
260 /*
261 * Shift msr to next position and increment vector
262 */
263 msr_shift <<= 1;
264 vec++;
265 }
266}
wdenkba56f622004-02-06 23:19:44 +0000267#endif
268
Stefan Roese846b0dd2005-08-08 12:42:22 +0200269#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000270/* Handler for UIC0 interrupt */
271void uic0_interrupt( void * parms)
272{
273 ulong uic_msr;
274 ulong msr_shift;
275 int vec;
276
277 /*
278 * Read masked interrupt status register to determine interrupt source
279 */
280 uic_msr = mfdcr(uicmsr);
281 msr_shift = uic_msr;
282 vec = 0;
283
284 while (msr_shift != 0) {
285 if (msr_shift & 0x80000000) {
286 /*
287 * Increment irq counter (for debug purpose only)
288 */
289 irq_vecs[vec].count++;
290
291 if (irq_vecs[vec].handler != NULL) {
292 /* call isr */
293 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
294 } else {
295 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
296 printf ("Masking bogus interrupt vector (uic0) 0x%x\n", vec);
297 }
298
299 /*
300 * After servicing the interrupt, we have to remove the status indicator.
301 */
302 mtdcr(uicsr, (0x80000000 >> vec));
303 }
304
305 /*
306 * Shift msr to next position and increment vector
307 */
308 msr_shift <<= 1;
309 vec++;
310 }
311}
312
Stefan Roese846b0dd2005-08-08 12:42:22 +0200313#endif /* CONFIG_440GX */
wdenkf780aa22002-09-18 19:21:21 +0000314
315#if defined(CONFIG_440)
316/* Handler for UIC1 interrupt */
317void uic1_interrupt( void * parms)
318{
319 ulong uic1_msr;
320 ulong msr_shift;
321 int vec;
322
323 /*
324 * Read masked interrupt status register to determine interrupt source
325 */
326 uic1_msr = mfdcr(uic1msr);
327 msr_shift = uic1_msr;
328 vec = 0;
329
330 while (msr_shift != 0) {
331 if (msr_shift & 0x80000000) {
332 /*
333 * Increment irq counter (for debug purpose only)
334 */
335 irq_vecs1[vec].count++;
336
337 if (irq_vecs1[vec].handler != NULL) {
338 /* call isr */
339 (*irq_vecs1[vec].handler)(irq_vecs1[vec].arg);
340 } else {
341 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> vec));
342 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
343 }
344
345 /*
346 * After servicing the interrupt, we have to remove the status indicator.
347 */
348 mtdcr(uic1sr, (0x80000000 >> vec));
349 }
350
351 /*
352 * Shift msr to next position and increment vector
353 */
354 msr_shift <<= 1;
355 vec++;
356 }
357}
358#endif /* defined(CONFIG_440) */
359
Stefan Roese846b0dd2005-08-08 12:42:22 +0200360#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000361/* Handler for UIC1 interrupt */
362void uic2_interrupt( void * parms)
363{
364 ulong uic2_msr;
365 ulong msr_shift;
366 int vec;
367
368 /*
369 * Read masked interrupt status register to determine interrupt source
370 */
371 uic2_msr = mfdcr(uic2msr);
372 msr_shift = uic2_msr;
373 vec = 0;
374
375 while (msr_shift != 0) {
376 if (msr_shift & 0x80000000) {
377 /*
378 * Increment irq counter (for debug purpose only)
379 */
380 irq_vecs2[vec].count++;
381
382 if (irq_vecs2[vec].handler != NULL) {
383 /* call isr */
384 (*irq_vecs2[vec].handler)(irq_vecs2[vec].arg);
385 } else {
386 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> vec));
387 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
388 }
389
390 /*
391 * After servicing the interrupt, we have to remove the status indicator.
392 */
393 mtdcr(uic2sr, (0x80000000 >> vec));
394 }
395
396 /*
397 * Shift msr to next position and increment vector
398 */
399 msr_shift <<= 1;
400 vec++;
401 }
402}
Stefan Roese846b0dd2005-08-08 12:42:22 +0200403#endif /* defined(CONFIG_440GX) */
wdenkba56f622004-02-06 23:19:44 +0000404
wdenkf780aa22002-09-18 19:21:21 +0000405/****************************************************************************/
406
407/*
408 * Install and free a interrupt handler.
409 */
410
wdenkba56f622004-02-06 23:19:44 +0000411void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg)
wdenkf780aa22002-09-18 19:21:21 +0000412{
413 struct irq_action *irqa = irq_vecs;
wdenkba56f622004-02-06 23:19:44 +0000414 int i = vec;
wdenkf780aa22002-09-18 19:21:21 +0000415
416#if defined(CONFIG_440)
Stefan Roese846b0dd2005-08-08 12:42:22 +0200417#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000418 if ((vec > 31) && (vec < 64)) {
419 i = vec - 32;
420 irqa = irq_vecs1;
421 } else if (vec > 63) {
422 i = vec - 64;
423 irqa = irq_vecs2;
424 }
Stefan Roese846b0dd2005-08-08 12:42:22 +0200425#else /* CONFIG_440GX */
wdenkf780aa22002-09-18 19:21:21 +0000426 if (vec > 31) {
427 i = vec - 32;
428 irqa = irq_vecs1;
429 }
Stefan Roese846b0dd2005-08-08 12:42:22 +0200430#endif /* CONFIG_440GX */
wdenkba56f622004-02-06 23:19:44 +0000431#endif /* CONFIG_440 */
wdenkf780aa22002-09-18 19:21:21 +0000432
Stefan Roesec157d8e2005-08-01 16:41:48 +0200433 /*
434 * print warning when replacing with a different irq vector
435 */
436 if ((irqa[i].handler != NULL) && (irqa[i].handler != handler)) {
wdenkf780aa22002-09-18 19:21:21 +0000437 printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
wdenkba56f622004-02-06 23:19:44 +0000438 vec, (uint) handler, (uint) irqa[i].handler);
wdenkf780aa22002-09-18 19:21:21 +0000439 }
440 irqa[i].handler = handler;
wdenkba56f622004-02-06 23:19:44 +0000441 irqa[i].arg = arg;
wdenkf780aa22002-09-18 19:21:21 +0000442
443#if defined(CONFIG_440)
Stefan Roese846b0dd2005-08-08 12:42:22 +0200444#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000445 if ((vec > 31) && (vec < 64))
446 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
447 else if (vec > 63)
448 mtdcr (uic2er, mfdcr (uic2er) | (0x80000000 >> i));
449 else
Stefan Roese846b0dd2005-08-08 12:42:22 +0200450#endif /* CONFIG_440GX */
wdenkba56f622004-02-06 23:19:44 +0000451 if (vec > 31)
452 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000453 else
454#endif
wdenkba56f622004-02-06 23:19:44 +0000455 mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000456#if 0
457 printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
458#endif
459}
460
wdenkba56f622004-02-06 23:19:44 +0000461void irq_free_handler (int vec)
wdenkf780aa22002-09-18 19:21:21 +0000462{
463 struct irq_action *irqa = irq_vecs;
wdenkba56f622004-02-06 23:19:44 +0000464 int i = vec;
wdenkf780aa22002-09-18 19:21:21 +0000465
466#if defined(CONFIG_440)
Stefan Roese846b0dd2005-08-08 12:42:22 +0200467#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000468 if ((vec > 31) && (vec < 64)) {
469 irqa = irq_vecs1;
470 i = vec - 32;
471 } else if (vec > 63) {
472 irqa = irq_vecs2;
473 i = vec - 64;
474 }
Stefan Roese846b0dd2005-08-08 12:42:22 +0200475#endif /* CONFIG_440GX */
wdenkf780aa22002-09-18 19:21:21 +0000476 if (vec > 31) {
477 irqa = irq_vecs1;
478 i = vec - 32;
479 }
480#endif
481
482#if 0
483 printf ("Free interrupt for vector %d ==> %p\n",
484 vec, irq_vecs[vec].handler);
485#endif
486
487#if defined(CONFIG_440)
Stefan Roese846b0dd2005-08-08 12:42:22 +0200488#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000489 if ((vec > 31) && (vec < 64))
490 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
491 else if (vec > 63)
492 mtdcr (uic2er, mfdcr (uic2er) & ~(0x80000000 >> i));
493 else
Stefan Roese846b0dd2005-08-08 12:42:22 +0200494#endif /* CONFIG_440GX */
wdenkf780aa22002-09-18 19:21:21 +0000495 if (vec > 31)
wdenkba56f622004-02-06 23:19:44 +0000496 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000497 else
498#endif
wdenkba56f622004-02-06 23:19:44 +0000499 mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000500
501 irqa[i].handler = NULL;
wdenkba56f622004-02-06 23:19:44 +0000502 irqa[i].arg = NULL;
wdenkf780aa22002-09-18 19:21:21 +0000503}
504
505/****************************************************************************/
506
wdenka8c7c702003-12-06 19:49:23 +0000507void timer_interrupt_cpu (struct pt_regs *regs)
wdenkf780aa22002-09-18 19:21:21 +0000508{
wdenka8c7c702003-12-06 19:49:23 +0000509 /* nothing to do here */
510 return;
wdenkf780aa22002-09-18 19:21:21 +0000511}
512
513/****************************************************************************/
514
wdenkf780aa22002-09-18 19:21:21 +0000515#if (CONFIG_COMMANDS & CFG_CMD_IRQ)
516
517/*******************************************************************************
518 *
519 * irqinfo - print information about PCI devices
520 *
521 */
522int
523do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
524{
525 int vec;
526
527 printf ("\nInterrupt-Information:\n");
528#if defined(CONFIG_440)
529 printf ("\nUIC 0\n");
530#endif
531 printf ("Nr Routine Arg Count\n");
532
533 for (vec=0; vec<32; vec++) {
534 if (irq_vecs[vec].handler != NULL) {
535 printf ("%02d %08lx %08lx %d\n",
536 vec,
537 (ulong)irq_vecs[vec].handler,
538 (ulong)irq_vecs[vec].arg,
539 irq_vecs[vec].count);
540 }
541 }
542
543#if defined(CONFIG_440)
544 printf ("\nUIC 1\n");
545 printf ("Nr Routine Arg Count\n");
546
wdenkba56f622004-02-06 23:19:44 +0000547 for (vec=0; vec<32; vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000548 if (irq_vecs1[vec].handler != NULL)
549 printf ("%02d %08lx %08lx %d\n",
550 vec+31, (ulong)irq_vecs1[vec].handler,
551 (ulong)irq_vecs1[vec].arg, irq_vecs1[vec].count);
552 }
553 printf("\n");
554#endif
wdenkba56f622004-02-06 23:19:44 +0000555
Stefan Roese846b0dd2005-08-08 12:42:22 +0200556#if defined(CONFIG_440GX)
wdenkba56f622004-02-06 23:19:44 +0000557 printf ("\nUIC 2\n");
558 printf ("Nr Routine Arg Count\n");
559
560 for (vec=0; vec<32; vec++) {
561 if (irq_vecs2[vec].handler != NULL)
562 printf ("%02d %08lx %08lx %d\n",
563 vec+63, (ulong)irq_vecs2[vec].handler,
564 (ulong)irq_vecs2[vec].arg, irq_vecs2[vec].count);
565 }
566 printf("\n");
567#endif
568
wdenkf780aa22002-09-18 19:21:21 +0000569 return 0;
570}
wdenkf780aa22002-09-18 19:21:21 +0000571#endif /* CONFIG_COMMANDS & CFG_CMD_IRQ */