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