Merge: Add support for AMCC 440SPe CPU based eval board (Yucca).
[platform/kernel/u-boot.git] / cpu / ppc4xx / interrupts.c
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  *
8  * (C) Copyright 2003 (440GX port)
9  * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com
10  *
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>
33 #include <asm/processor.h>
34 #include <ppc4xx.h>
35 #include <ppc_asm.tmpl>
36 #include <commproc.h>
37 #include "vecnum.h"
38
39 DECLARE_GLOBAL_DATA_PTR;
40
41 /****************************************************************************/
42
43 /*
44  * CPM interrupt vector functions.
45  */
46 struct  irq_action {
47         interrupt_handler_t *handler;
48         void *arg;
49         int count;
50 };
51
52 static struct irq_action irq_vecs[32];
53 void uic0_interrupt( void * parms); /* UIC0 handler */
54
55 #if defined(CONFIG_440)
56 static struct irq_action irq_vecs1[32]; /* For UIC1 */
57
58 void uic1_interrupt( void * parms); /* UIC1 handler */
59
60 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
61 static struct irq_action irq_vecs2[32]; /* For UIC2 */
62 void uic2_interrupt( void * parms); /* UIC2 handler */
63 #endif /* CONFIG_440GX CONFIG_440SPE */
64
65 #if defined(CONFIG_440SPE)
66 static struct irq_action irq_vecs3[32]; /* For UIC3 */
67 void uic3_interrupt( void * parms); /* UIC3 handler */
68 #endif /* CONFIG_440SPE */
69
70 #endif /* CONFIG_440 */
71
72 /****************************************************************************/
73 #if defined(CONFIG_440)
74
75 /* SPRN changed in 440 */
76 static __inline__ void set_evpr(unsigned long val)
77 {
78         asm volatile("mtspr 0x03f,%0" : : "r" (val));
79 }
80
81 #else /* !defined(CONFIG_440) */
82
83 static __inline__ void set_pit(unsigned long val)
84 {
85         asm volatile("mtpit %0" : : "r" (val));
86 }
87
88
89 static __inline__ void set_tcr(unsigned long val)
90 {
91         asm volatile("mttcr %0" : : "r" (val));
92 }
93
94
95 static __inline__ void set_evpr(unsigned long val)
96 {
97         asm volatile("mtevpr %0" : : "r" (val));
98 }
99 #endif /* defined(CONFIG_440 */
100
101 /****************************************************************************/
102
103 int interrupt_init_cpu (unsigned *decrementer_count)
104 {
105         int vec;
106         unsigned long val;
107
108         /* decrementer is automatically reloaded */
109         *decrementer_count = 0;
110
111         /*
112          * Mark all irqs as free
113          */
114         for (vec=0; vec<32; vec++) {
115                 irq_vecs[vec].handler = NULL;
116                 irq_vecs[vec].arg = NULL;
117                 irq_vecs[vec].count = 0;
118 #if defined(CONFIG_440)
119                 irq_vecs1[vec].handler = NULL;
120                 irq_vecs1[vec].arg = NULL;
121                 irq_vecs1[vec].count = 0;
122 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
123                 irq_vecs2[vec].handler = NULL;
124                 irq_vecs2[vec].arg = NULL;
125                 irq_vecs2[vec].count = 0;
126 #endif /* CONFIG_440GX */
127 #if defined(CONFIG_440SPE)
128                 irq_vecs3[vec].handler = NULL;
129                 irq_vecs3[vec].arg = NULL;
130                 irq_vecs3[vec].count = 0;
131 #endif /* CONFIG_440SPE */
132 #endif
133         }
134
135 #ifdef CONFIG_4xx
136         /*
137          * Init PIT
138          */
139 #if defined(CONFIG_440)
140         val = mfspr( tcr );
141         val &= (~0x04400000);           /* clear DIS & ARE */
142         mtspr( tcr, val );
143         mtspr( dec, 0 );                /* Prevent exception after TSR clear*/
144         mtspr( decar, 0 );              /* clear reload */
145         mtspr( tsr, 0x08000000 );       /* clear DEC status */
146         val = gd->bd->bi_intfreq/1000;  /* 1 msec */
147         mtspr( decar, val );            /* Set auto-reload value */
148         mtspr( dec, val );              /* Set inital val */
149 #else
150         set_pit(gd->bd->bi_intfreq / 1000);
151 #endif
152 #endif  /* CONFIG_4xx */
153
154 #ifdef CONFIG_ADCIOP
155         /*
156          * Init PIT
157          */
158         set_pit(66000);
159 #endif
160
161         /*
162          * Enable PIT
163          */
164         val = mfspr(tcr);
165         val |= 0x04400000;
166         mtspr(tcr, val);
167
168         /*
169          * Set EVPR to 0
170          */
171         set_evpr(0x00000000);
172
173 #if defined(CONFIG_440)
174 #if !defined(CONFIG_440GX)
175         /* Install the UIC1 handlers */
176         irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0);
177         irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0);
178 #endif
179 #endif
180
181 #if defined(CONFIG_440GX)
182         /* Take the GX out of compatibility mode
183          * Travis Sawyer, 9 Mar 2004
184          * NOTE: 440gx user manual inconsistency here
185          *       Compatibility mode and Ethernet Clock select are not
186          *       correct in the manual
187          */
188         mfsdr(sdr_mfr, val);
189         val &= ~0x10000000;
190         mtsdr(sdr_mfr,val);
191
192         /* Enable UIC interrupts via UIC Base Enable Register */
193         mtdcr(uicb0sr, UICB0_ALL);
194         mtdcr(uicb0er, 0x54000000);
195         /* None are critical */
196         mtdcr(uicb0cr, 0);
197 #endif
198
199         return (0);
200 }
201
202 /****************************************************************************/
203
204 /*
205  * Handle external interrupts
206  */
207 #if defined(CONFIG_440GX)
208 void external_interrupt(struct pt_regs *regs)
209 {
210         ulong uic_msr;
211
212         /*
213          * Read masked interrupt status register to determine interrupt source
214          */
215         /* 440 GX uses base uic register */
216         uic_msr = mfdcr(uicb0msr);
217
218         if ( (UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr) )
219                 uic0_interrupt(0);
220
221         if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
222                 uic1_interrupt(0);
223
224         if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
225                 uic2_interrupt(0);
226
227         mtdcr(uicb0sr, uic_msr);
228
229         return;
230
231 } /* external_interrupt CONFIG_440GX */
232
233 #elif defined(CONFIG_440SPE)
234 void external_interrupt(struct pt_regs *regs)
235 {
236         ulong uic_msr;
237
238         /*
239          * Read masked interrupt status register to determine interrupt source
240          */
241         /* 440 SPe uses base uic register */
242         uic_msr = mfdcr(uic0msr);
243
244         if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
245                 uic1_interrupt(0);
246
247         if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
248                 uic2_interrupt(0);
249
250         if ( (UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr) )
251                 uic3_interrupt(0);
252
253         if (uic_msr & ~(UICB0_ALL))
254                 uic0_interrupt(0);
255
256         mtdcr(uic0sr, uic_msr);
257
258         return;
259 } /* external_interrupt CONFIG_440SPE */
260
261 #else
262
263 void external_interrupt(struct pt_regs *regs)
264 {
265         ulong uic_msr;
266         ulong msr_shift;
267         int vec;
268
269         /*
270          * Read masked interrupt status register to determine interrupt source
271          */
272         uic_msr = mfdcr(uicmsr);
273         msr_shift = uic_msr;
274         vec = 0;
275
276         while (msr_shift != 0) {
277                 if (msr_shift & 0x80000000) {
278                         /*
279                          * Increment irq counter (for debug purpose only)
280                          */
281                         irq_vecs[vec].count++;
282
283                         if (irq_vecs[vec].handler != NULL) {
284                                 /* call isr */
285                                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
286                         } else {
287                                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
288                                 printf ("Masking bogus interrupt vector 0x%x\n", vec);
289                         }
290
291                         /*
292                          * After servicing the interrupt, we have to remove the status indicator.
293                          */
294                         mtdcr(uicsr, (0x80000000 >> vec));
295                 }
296
297                 /*
298                  * Shift msr to next position and increment vector
299                  */
300                 msr_shift <<= 1;
301                 vec++;
302         }
303 }
304 #endif
305
306 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
307 /* Handler for UIC0 interrupt */
308 void uic0_interrupt( void * parms)
309 {
310         ulong uic_msr;
311         ulong msr_shift;
312         int vec;
313
314         /*
315          * Read masked interrupt status register to determine interrupt source
316          */
317         uic_msr = mfdcr(uicmsr);
318         msr_shift = uic_msr;
319         vec = 0;
320
321         while (msr_shift != 0) {
322                 if (msr_shift & 0x80000000) {
323                         /*
324                          * Increment irq counter (for debug purpose only)
325                          */
326                         irq_vecs[vec].count++;
327
328                         if (irq_vecs[vec].handler != NULL) {
329                                 /* call isr */
330                                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
331                         } else {
332                                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
333                                 printf ("Masking bogus interrupt vector (uic0) 0x%x\n", vec);
334                         }
335
336                         /*
337                          * After servicing the interrupt, we have to remove the status indicator.
338                          */
339                         mtdcr(uicsr, (0x80000000 >> vec));
340                 }
341
342                 /*
343                  * Shift msr to next position and increment vector
344                  */
345                 msr_shift <<= 1;
346                 vec++;
347         }
348 }
349
350 #endif /* CONFIG_440GX */
351
352 #if defined(CONFIG_440)
353 /* Handler for UIC1 interrupt */
354 void uic1_interrupt( void * parms)
355 {
356         ulong uic1_msr;
357         ulong msr_shift;
358         int vec;
359
360         /*
361          * Read masked interrupt status register to determine interrupt source
362          */
363         uic1_msr = mfdcr(uic1msr);
364         msr_shift = uic1_msr;
365         vec = 0;
366
367         while (msr_shift != 0) {
368                 if (msr_shift & 0x80000000) {
369                         /*
370                          * Increment irq counter (for debug purpose only)
371                          */
372                         irq_vecs1[vec].count++;
373
374                         if (irq_vecs1[vec].handler != NULL) {
375                                 /* call isr */
376                                 (*irq_vecs1[vec].handler)(irq_vecs1[vec].arg);
377                         } else {
378                                 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> vec));
379                                 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
380                         }
381
382                         /*
383                          * After servicing the interrupt, we have to remove the status indicator.
384                          */
385                         mtdcr(uic1sr, (0x80000000 >> vec));
386                 }
387
388                 /*
389                  * Shift msr to next position and increment vector
390                  */
391                 msr_shift <<= 1;
392                 vec++;
393         }
394 }
395 #endif /* defined(CONFIG_440) */
396
397 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
398 /* Handler for UIC2 interrupt */
399 void uic2_interrupt( void * parms)
400 {
401         ulong uic2_msr;
402         ulong msr_shift;
403         int vec;
404
405         /*
406          * Read masked interrupt status register to determine interrupt source
407          */
408         uic2_msr = mfdcr(uic2msr);
409         msr_shift = uic2_msr;
410         vec = 0;
411
412         while (msr_shift != 0) {
413                 if (msr_shift & 0x80000000) {
414                         /*
415                          * Increment irq counter (for debug purpose only)
416                          */
417                         irq_vecs2[vec].count++;
418
419                         if (irq_vecs2[vec].handler != NULL) {
420                                 /* call isr */
421                                 (*irq_vecs2[vec].handler)(irq_vecs2[vec].arg);
422                         } else {
423                                 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> vec));
424                                 printf ("Masking bogus interrupt vector (uic2) 0x%x\n", vec);
425                         }
426
427                         /*
428                          * After servicing the interrupt, we have to remove the status indicator.
429                          */
430                         mtdcr(uic2sr, (0x80000000 >> vec));
431                 }
432
433                 /*
434                  * Shift msr to next position and increment vector
435                  */
436                 msr_shift <<= 1;
437                 vec++;
438         }
439 }
440 #endif /* defined(CONFIG_440GX) */
441
442 #if defined(CONFIG_440SPE)
443 /* Handler for UIC3 interrupt */
444 void uic3_interrupt( void * parms)
445 {
446         ulong uic3_msr;
447         ulong msr_shift;
448         int vec;
449
450         /*
451          * Read masked interrupt status register to determine interrupt source
452          */
453         uic3_msr = mfdcr(uic3msr);
454         msr_shift = uic3_msr;
455         vec = 0;
456
457         while (msr_shift != 0) {
458                 if (msr_shift & 0x80000000) {
459                         /*
460                          * Increment irq counter (for debug purpose only)
461                          */
462                         irq_vecs3[vec].count++;
463
464                         if (irq_vecs3[vec].handler != NULL) {
465                                 /* call isr */
466                                 (*irq_vecs3[vec].handler)(irq_vecs3[vec].arg);
467                         } else {
468                                 mtdcr(uic3er, mfdcr(uic3er) & ~(0x80000000 >> vec));
469                                 printf ("Masking bogus interrupt vector (uic3) 0x%x\n", vec);
470                         }
471
472                         /*
473                          * After servicing the interrupt, we have to remove the status indicator.
474                          */
475                         mtdcr(uic3sr, (0x80000000 >> vec));
476                 }
477
478                 /*
479                  * Shift msr to next position and increment vector
480                  */
481                 msr_shift <<= 1;
482                 vec++;
483         }
484 }
485 #endif /* defined(CONFIG_440SPE) */
486
487 /****************************************************************************/
488
489 /*
490  * Install and free a interrupt handler.
491  */
492
493 void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg)
494 {
495         struct irq_action *irqa = irq_vecs;
496         int i = vec;
497
498 #if defined(CONFIG_440)
499 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
500         if ((vec > 31) && (vec < 64)) {
501                 i = vec - 32;
502                 irqa = irq_vecs1;
503         } else if (vec > 63) {
504                 i = vec - 64;
505                 irqa = irq_vecs2;
506         }
507 #else  /* CONFIG_440GX */
508         if (vec > 31) {
509                 i = vec - 32;
510                 irqa = irq_vecs1;
511         }
512 #endif /* CONFIG_440GX */
513 #endif /* CONFIG_440 */
514
515         /*
516          * print warning when replacing with a different irq vector
517          */
518         if ((irqa[i].handler != NULL) && (irqa[i].handler != handler)) {
519                 printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
520                         vec, (uint) handler, (uint) irqa[i].handler);
521         }
522         irqa[i].handler = handler;
523         irqa[i].arg = arg;
524
525 #if defined(CONFIG_440)
526 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
527         if ((vec > 31) && (vec < 64))
528                 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
529         else if (vec > 63)
530                 mtdcr (uic2er, mfdcr (uic2er) | (0x80000000 >> i));
531         else
532 #endif /* CONFIG_440GX */
533         if (vec > 31)
534                 mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
535         else
536 #endif
537                 mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i));
538 #if 0
539         printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
540 #endif
541 }
542
543 void irq_free_handler (int vec)
544 {
545         struct irq_action *irqa = irq_vecs;
546         int i = vec;
547
548 #if defined(CONFIG_440)
549 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
550         if ((vec > 31) && (vec < 64)) {
551                 irqa = irq_vecs1;
552                 i = vec - 32;
553         } else if (vec > 63) {
554                 irqa = irq_vecs2;
555                 i = vec - 64;
556         }
557 #endif /* CONFIG_440GX */
558         if (vec > 31) {
559                 irqa = irq_vecs1;
560                 i = vec - 32;
561         }
562 #endif
563
564 #if 0
565         printf ("Free interrupt for vector %d ==> %p\n",
566                 vec, irq_vecs[vec].handler);
567 #endif
568
569 #if defined(CONFIG_440)
570 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
571         if ((vec > 31) && (vec < 64))
572                 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
573         else if (vec > 63)
574                 mtdcr (uic2er, mfdcr (uic2er) & ~(0x80000000 >> i));
575         else
576 #endif /* CONFIG_440GX */
577         if (vec > 31)
578                 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
579         else
580 #endif
581                 mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i));
582
583         irqa[i].handler = NULL;
584         irqa[i].arg = NULL;
585 }
586
587 /****************************************************************************/
588
589 void timer_interrupt_cpu (struct pt_regs *regs)
590 {
591         /* nothing to do here */
592         return;
593 }
594
595 /****************************************************************************/
596
597 #if (CONFIG_COMMANDS & CFG_CMD_IRQ)
598
599 /*******************************************************************************
600  *
601  * irqinfo - print information about PCI devices
602  *
603  */
604 int
605 do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
606 {
607         int vec;
608
609         printf ("\nInterrupt-Information:\n");
610 #if defined(CONFIG_440)
611         printf ("\nUIC 0\n");
612 #endif
613         printf ("Nr  Routine   Arg       Count\n");
614
615         for (vec=0; vec<32; vec++) {
616                 if (irq_vecs[vec].handler != NULL) {
617                         printf ("%02d  %08lx  %08lx  %d\n",
618                                 vec,
619                                 (ulong)irq_vecs[vec].handler,
620                                 (ulong)irq_vecs[vec].arg,
621                                 irq_vecs[vec].count);
622                 }
623         }
624
625 #if defined(CONFIG_440)
626         printf ("\nUIC 1\n");
627         printf ("Nr  Routine   Arg       Count\n");
628
629         for (vec=0; vec<32; vec++) {
630                 if (irq_vecs1[vec].handler != NULL)
631                         printf ("%02d  %08lx  %08lx  %d\n",
632                                 vec+31, (ulong)irq_vecs1[vec].handler,
633                                 (ulong)irq_vecs1[vec].arg, irq_vecs1[vec].count);
634         }
635         printf("\n");
636 #endif
637
638 #if defined(CONFIG_440GX) || defined(CONFIG_440SPE)
639         printf ("\nUIC 2\n");
640         printf ("Nr  Routine   Arg       Count\n");
641
642         for (vec=0; vec<32; vec++) {
643                 if (irq_vecs2[vec].handler != NULL)
644                         printf ("%02d  %08lx  %08lx  %d\n",
645                                 vec+63, (ulong)irq_vecs2[vec].handler,
646                                 (ulong)irq_vecs2[vec].arg, irq_vecs2[vec].count);
647         }
648         printf("\n");
649 #endif
650
651 #if defined(CONFIG_440SPE)
652         printf ("\nUIC 3\n");
653         printf ("Nr  Routine   Arg       Count\n");
654
655         for (vec=0; vec<32; vec++) {
656                 if (irq_vecs3[vec].handler != NULL)
657                         printf ("%02d  %08lx  %08lx  %d\n",
658                                         vec+63, (ulong)irq_vecs3[vec].handler,
659                                         (ulong)irq_vecs3[vec].arg, irq_vecs3[vec].count);
660         }
661         printf("\n");
662 #endif
663
664         return 0;
665 }
666 #endif  /* CONFIG_COMMANDS & CFG_CMD_IRQ */