Initial revision
[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  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26
27 #include <common.h>
28 #include <watchdog.h>
29 #include <command.h>
30 #include <cmd_boot.h>
31 #include <asm/processor.h>
32 #include <ppc4xx.h>
33 #include <ppc_asm.tmpl>
34 #include <commproc.h>
35 #include "vecnum.h"
36
37 /****************************************************************************/
38
39 unsigned decrementer_count;             /* count value for 1e6/HZ microseconds */
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
54 #if defined(CONFIG_440)
55 static struct irq_action irq_vecs1[32]; /* For UIC1 */
56
57 void uic1_interrupt( void * parms); /* UIC1 handler */
58 #endif
59
60 /****************************************************************************/
61
62 static __inline__ unsigned long get_msr(void)
63 {
64         unsigned long msr;
65
66         asm volatile("mfmsr %0" : "=r" (msr) :);
67         return msr;
68 }
69
70 static __inline__ void set_msr(unsigned long msr)
71 {
72         asm volatile("mtmsr %0" : : "r" (msr));
73 }
74
75 #if defined(CONFIG_440)
76
77 /* SPRN changed in 440 */
78 static __inline__ void set_evpr(unsigned long val)
79 {
80         asm volatile("mtspr 0x03f,%0" : : "r" (val));
81 }
82
83 #else /* !defined(CONFIG_440) */
84
85 static __inline__ unsigned long get_dec(void)
86 {
87         unsigned long val;
88
89         asm volatile("mfdec %0" : "=r" (val) :);
90         return val;
91 }
92
93
94 static __inline__ void set_dec(unsigned long val)
95 {
96         asm volatile("mtdec %0" : : "r" (val));
97 }
98
99
100 static __inline__ void set_pit(unsigned long val)
101 {
102         asm volatile("mtpit %0" : : "r" (val));
103 }
104
105
106 static __inline__ void set_tcr(unsigned long val)
107 {
108         asm volatile("mttcr %0" : : "r" (val));
109 }
110
111
112 static __inline__ void set_evpr(unsigned long val)
113 {
114         asm volatile("mtevpr %0" : : "r" (val));
115 }
116 #endif /* defined(CONFIG_440 */
117
118
119 void enable_interrupts (void)
120 {
121         set_msr (get_msr() | MSR_EE);
122 }
123
124 /* returns flag if MSR_EE was set before */
125 int disable_interrupts (void)
126 {
127         ulong msr = get_msr();
128         set_msr (msr & ~MSR_EE);
129         return ((msr & MSR_EE) != 0);
130 }
131
132 /****************************************************************************/
133
134 int interrupt_init(void)
135 {
136         DECLARE_GLOBAL_DATA_PTR;
137
138         int vec;
139         unsigned long val;
140
141         /*
142          * Mark all irqs as free
143          */
144         for (vec=0; vec<32; vec++) {
145                 irq_vecs[vec].handler = NULL;
146                 irq_vecs[vec].arg = NULL;
147                 irq_vecs[vec].count = 0;
148 #if defined(CONFIG_440)
149                 irq_vecs1[vec].handler = NULL;
150                 irq_vecs1[vec].arg = NULL;
151                 irq_vecs1[vec].count = 0;
152 #endif
153         }
154
155 #ifdef CONFIG_4xx
156         /*
157          * Init PIT
158          */
159 #if defined(CONFIG_440)
160         val = mfspr( tcr );
161         val &= (~0x04400000);           /* clear DIS & ARE */
162         mtspr( tcr, val );
163         mtspr( dec, 0 );                /* Prevent exception after TSR clear*/
164         mtspr( decar, 0 );              /* clear reload */
165         mtspr( tsr, 0x08000000 );       /* clear DEC status */
166         val = gd->bd->bi_intfreq/100;   /* 10 msec */
167         mtspr( decar, val );            /* Set auto-reload value */
168         mtspr( dec, val );              /* Set inital val */
169 #else
170         set_pit(gd->bd->bi_intfreq / 1000);
171 #endif
172 #endif  /* CONFIG_4xx */
173
174 #ifdef CONFIG_ADCIOP
175         /*
176          * Init PIT
177          */
178         set_pit(66000);
179 #endif
180
181         /*
182          * Enable PIT
183          */
184         val = mfspr(tcr);
185         val |= 0x04400000;
186         mtspr(tcr, val);
187
188         /*
189          * Set EVPR to 0
190          */
191         set_evpr(0x00000000);
192
193 #if defined(CONFIG_440)
194         /* Install the UIC1 handlers */
195         irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0);
196         irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0);
197 #endif
198         /*
199          * Enable external interrupts (including PIT)
200          */
201         set_msr (get_msr() | MSR_EE);
202
203         return (0);
204 }
205
206 /****************************************************************************/
207
208 /*
209  * Handle external interrupts
210  */
211 void external_interrupt(struct pt_regs *regs)
212 {
213         ulong uic_msr;
214         ulong msr_shift;
215         int vec;
216
217         /*
218          * Read masked interrupt status register to determine interrupt source
219          */
220         uic_msr = mfdcr(uicmsr);
221         msr_shift = uic_msr;
222         vec = 0;
223
224         while (msr_shift != 0) {
225                 if (msr_shift & 0x80000000) {
226                         /*
227                          * Increment irq counter (for debug purpose only)
228                          */
229                         irq_vecs[vec].count++;
230
231                         if (irq_vecs[vec].handler != NULL) {
232                                 /* call isr */
233                                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
234                         } else {
235                                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
236                                 printf ("Masking bogus interrupt vector 0x%x\n", vec);
237                         }
238
239                         /*
240                          * After servicing the interrupt, we have to remove the status indicator.
241                          */
242                         mtdcr(uicsr, (0x80000000 >> vec));
243                 }
244
245                 /*
246                  * Shift msr to next position and increment vector
247                  */
248                 msr_shift <<= 1;
249                 vec++;
250         }
251 }
252
253 #if defined(CONFIG_440)
254 /* Handler for UIC1 interrupt */
255 void uic1_interrupt( void * parms)
256 {
257         ulong uic1_msr;
258         ulong msr_shift;
259         int vec;
260
261         /*
262          * Read masked interrupt status register to determine interrupt source
263          */
264         uic1_msr = mfdcr(uic1msr);
265         msr_shift = uic1_msr;
266         vec = 0;
267
268         while (msr_shift != 0) {
269                 if (msr_shift & 0x80000000) {
270                         /*
271                          * Increment irq counter (for debug purpose only)
272                          */
273                         irq_vecs1[vec].count++;
274
275                         if (irq_vecs1[vec].handler != NULL) {
276                                 /* call isr */
277                                 (*irq_vecs1[vec].handler)(irq_vecs1[vec].arg);
278                         } else {
279                                 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> vec));
280                                 printf ("Masking bogus interrupt vector (uic1) 0x%x\n", vec);
281                         }
282
283                         /*
284                          * After servicing the interrupt, we have to remove the status indicator.
285                          */
286                         mtdcr(uic1sr, (0x80000000 >> vec));
287                 }
288
289                 /*
290                  * Shift msr to next position and increment vector
291                  */
292                 msr_shift <<= 1;
293                 vec++;
294         }
295 }
296 #endif /* defined(CONFIG_440) */
297
298 /****************************************************************************/
299
300 /*
301  * Install and free a interrupt handler.
302  */
303
304 void
305 irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
306 {
307         struct irq_action *irqa = irq_vecs;
308         int   i = vec;
309
310 #if defined(CONFIG_440)
311         if (vec > 31) {
312                 i = vec - 32;
313                 irqa = irq_vecs1;
314         }
315 #endif
316
317         if (irqa[i].handler != NULL) {
318                 printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
319                         vec, (uint)handler, (uint)irqa[i].handler);
320         }
321         irqa[i].handler = handler;
322         irqa[i].arg     = arg;
323
324 #if defined(CONFIG_440)
325         if( vec > 31 )
326                 mtdcr(uic1er, mfdcr(uic1er) | (0x80000000 >> i));
327         else
328 #endif
329                 mtdcr(uicer, mfdcr(uicer) | (0x80000000 >> i));
330 #if 0
331         printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
332 #endif
333 }
334
335 void
336 irq_free_handler(int vec)
337 {
338         struct irq_action *irqa = irq_vecs;
339         int   i = vec;
340
341 #if defined(CONFIG_440)
342         if (vec > 31) {
343                 irqa = irq_vecs1;
344                 i = vec - 32;
345         }
346 #endif
347
348 #if 0
349         printf ("Free interrupt for vector %d ==> %p\n",
350                 vec, irq_vecs[vec].handler);
351 #endif
352
353 #if defined(CONFIG_440)
354         if (vec > 31)
355                 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> i));
356         else
357 #endif
358                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> i));
359
360         irqa[i].handler = NULL;
361         irqa[i].arg     = NULL;
362 }
363
364 /****************************************************************************/
365
366
367 volatile ulong timestamp = 0;
368
369 /*
370  * timer_interrupt - gets called when the decrementer overflows,
371  * with interrupts disabled.
372  * Trivial implementation - no need to be really accurate.
373  */
374 void timer_interrupt(struct pt_regs *regs)
375 {
376 #if 0
377         printf ("*** Timer Interrupt *** ");
378 #endif
379         timestamp++;
380
381 #if defined(CONFIG_WATCHDOG)
382         if ((timestamp % 1000) == 0)
383                 reset_4xx_watchdog();
384 #endif /* CONFIG_WATCHDOG */
385 }
386
387 /****************************************************************************/
388
389 void reset_timer (void)
390 {
391         timestamp = 0;
392 }
393
394 ulong get_timer (ulong base)
395 {
396         return (timestamp - base);
397 }
398
399 void set_timer (ulong t)
400 {
401         timestamp = t;
402 }
403
404 /****************************************************************************/
405
406
407 #if (CONFIG_COMMANDS & CFG_CMD_IRQ)
408
409 /*******************************************************************************
410  *
411  * irqinfo - print information about PCI devices
412  *
413  */
414 int
415 do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
416 {
417         int vec;
418
419         printf ("\nInterrupt-Information:\n");
420 #if defined(CONFIG_440)
421         printf ("\nUIC 0\n");
422 #endif
423         printf ("Nr  Routine   Arg       Count\n");
424
425         for (vec=0; vec<32; vec++) {
426                 if (irq_vecs[vec].handler != NULL) {
427                         printf ("%02d  %08lx  %08lx  %d\n",
428                                 vec,
429                                 (ulong)irq_vecs[vec].handler,
430                                 (ulong)irq_vecs[vec].arg,
431                                 irq_vecs[vec].count);
432                 }
433         }
434
435 #if defined(CONFIG_440)
436         printf ("\nUIC 1\n");
437         printf ("Nr  Routine   Arg       Count\n");
438
439         for (vec=0; vec<32; vec++)
440         {
441                 if (irq_vecs1[vec].handler != NULL)
442                         printf ("%02d  %08lx  %08lx  %d\n",
443                                 vec+31, (ulong)irq_vecs1[vec].handler,
444                                 (ulong)irq_vecs1[vec].arg, irq_vecs1[vec].count);
445         }
446         printf("\n");
447 #endif
448         return 0;
449 }
450
451
452 #endif  /* CONFIG_COMMANDS & CFG_CMD_IRQ */