Patch by Travis Sawyer, 30 Dec 2003:
[pandora-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 /****************************************************************************/
40
41 /*
42  * CPM interrupt vector functions.
43  */
44 struct  irq_action {
45         interrupt_handler_t *handler;
46         void *arg;
47         int count;
48 };
49
50 static struct irq_action irq_vecs[32];
51
52 #if defined(CONFIG_440)
53 static struct irq_action irq_vecs1[32]; /* For UIC1 */
54
55 void uic1_interrupt( void * parms); /* UIC1 handler */
56
57 #if defined(CONFIG_440_GX)
58 static struct irq_action irq_vecs2[32]; /* For UIC2 */
59
60 void uic0_interrupt( void * parms); /* UIC0 handler */
61 void uic2_interrupt( void * parms); /* UIC2 handler */
62 #endif /* CONFIG_440_GX */
63
64 #endif /* CONFIG_440 */
65
66 /****************************************************************************/
67 #if defined(CONFIG_440)
68
69 /* SPRN changed in 440 */
70 static __inline__ void set_evpr(unsigned long val)
71 {
72         asm volatile("mtspr 0x03f,%0" : : "r" (val));
73 }
74
75 #else /* !defined(CONFIG_440) */
76
77 static __inline__ void set_pit(unsigned long val)
78 {
79         asm volatile("mtpit %0" : : "r" (val));
80 }
81
82
83 static __inline__ void set_tcr(unsigned long val)
84 {
85         asm volatile("mttcr %0" : : "r" (val));
86 }
87
88
89 static __inline__ void set_evpr(unsigned long val)
90 {
91         asm volatile("mtevpr %0" : : "r" (val));
92 }
93 #endif /* defined(CONFIG_440 */
94
95 /****************************************************************************/
96
97 int interrupt_init_cpu (unsigned *decrementer_count)
98 {
99         DECLARE_GLOBAL_DATA_PTR;
100
101         int vec;
102         unsigned long val;
103
104         /* decrementer is automatically reloaded */
105         *decrementer_count = 0;
106
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;
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 */
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)
165 #if !defined(CONFIG_440_GX)
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
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
177
178         return (0);
179 }
180
181 /****************************************************************************/
182
183 /*
184  * Handle external interrupts
185  */
186 #if defined(CONFIG_440_GX)
187 void 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
209 void 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 }
250 #endif
251
252 #if defined(CONFIG_440_GX)
253 /* Handler for UIC0 interrupt */
254 void 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 */
297
298 #if defined(CONFIG_440)
299 /* Handler for UIC1 interrupt */
300 void 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
343 #if defined(CONFIG_440_GX)
344 /* Handler for UIC1 interrupt */
345 void 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
388 /****************************************************************************/
389
390 /*
391  * Install and free a interrupt handler.
392  */
393
394 void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg)
395 {
396         struct irq_action *irqa = irq_vecs;
397         int i = vec;
398
399 #if defined(CONFIG_440)
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 */
409         if (vec > 31) {
410                 i = vec - 32;
411                 irqa = irq_vecs1;
412         }
413 #endif /* CONFIG_440_GX */
414 #endif /* CONFIG_440 */
415
416         if (irqa[i].handler != NULL) {
417                 printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
418                         vec, (uint) handler, (uint) irqa[i].handler);
419         }
420         irqa[i].handler = handler;
421         irqa[i].arg = arg;
422
423 #if defined(CONFIG_440)
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));
433         else
434 #endif
435                 mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i));
436 #if 0
437         printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
438 #endif
439 }
440
441 void irq_free_handler (int vec)
442 {
443         struct irq_action *irqa = irq_vecs;
444         int i = vec;
445
446 #if defined(CONFIG_440)
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 */
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)
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 */
475         if (vec > 31)
476                 mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
477         else
478 #endif
479                 mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i));
480
481         irqa[i].handler = NULL;
482         irqa[i].arg = NULL;
483 }
484
485 /****************************************************************************/
486
487 void timer_interrupt_cpu (struct pt_regs *regs)
488 {
489         /* nothing to do here */
490         return;
491 }
492
493 /****************************************************************************/
494
495 #if (CONFIG_COMMANDS & CFG_CMD_IRQ)
496
497 /*******************************************************************************
498  *
499  * irqinfo - print information about PCI devices
500  *
501  */
502 int
503 do_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
527         for (vec=0; vec<32; vec++) {
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
535
536 #if defined(CONFIG_440_GX)
537         printf ("\nUIC 2\n");
538         printf ("Nr  Routine   Arg       Count\n");
539
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 }
551 #endif  /* CONFIG_COMMANDS & CFG_CMD_IRQ */