698bcb57d7ce9ae5ba9929dafb5ecd3c250672ad
[oweals/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 <asm/ppc4xx-intvec.h>
38
39 DECLARE_GLOBAL_DATA_PTR;
40
41 /*
42  * Define the number of UIC's
43  */
44 #if defined(CONFIG_440SPE) || \
45     defined(CONFIG_460EX) || defined(CONFIG_460GT)
46 #define UIC_MAX         4
47 #elif defined(CONFIG_440GX) || \
48     defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
49     defined(CONFIG_405EX)
50 #define UIC_MAX         3
51 #elif defined(CONFIG_440GP) || defined(CONFIG_440SP) || \
52     defined(CONFIG_440EP) || defined(CONFIG_440GR)
53 #define UIC_MAX         2
54 #else
55 #define UIC_MAX         1
56 #endif
57
58 /*
59  * CPM interrupt vector functions.
60  */
61 struct  irq_action {
62         interrupt_handler_t *handler;
63         void *arg;
64         int count;
65 };
66
67 static struct irq_action irq_vecs[UIC_MAX * 32];
68
69 u32 get_dcr(u16);
70 void set_dcr(u16, u32);
71
72 #if (UIC_MAX > 1) && !defined(CONFIG_440GX)
73 static void uic_cascade_interrupt(void *para);
74 #endif
75
76 #if defined(CONFIG_440)
77
78 /* SPRN changed in 440 */
79 static __inline__ void set_evpr(unsigned long val)
80 {
81         asm volatile("mtspr 0x03f,%0" : : "r" (val));
82 }
83
84 #else /* !defined(CONFIG_440) */
85
86 static __inline__ void set_pit(unsigned long val)
87 {
88         asm volatile("mtpit %0" : : "r" (val));
89 }
90
91
92 static __inline__ void set_tcr(unsigned long val)
93 {
94         asm volatile("mttcr %0" : : "r" (val));
95 }
96
97
98 static __inline__ void set_evpr(unsigned long val)
99 {
100         asm volatile("mtevpr %0" : : "r" (val));
101 }
102 #endif /* defined(CONFIG_440 */
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 < (UIC_MAX * 32); vec++) {
116                 irq_vecs[vec].handler = NULL;
117                 irq_vecs[vec].arg = NULL;
118                 irq_vecs[vec].count = 0;
119         }
120
121 #ifdef CONFIG_4xx
122         /*
123          * Init PIT
124          */
125 #if defined(CONFIG_440)
126         val = mfspr( tcr );
127         val &= (~0x04400000);           /* clear DIS & ARE */
128         mtspr( tcr, val );
129         mtspr( dec, 0 );                /* Prevent exception after TSR clear*/
130         mtspr( decar, 0 );              /* clear reload */
131         mtspr( tsr, 0x08000000 );       /* clear DEC status */
132         val = gd->bd->bi_intfreq/1000;  /* 1 msec */
133         mtspr( decar, val );            /* Set auto-reload value */
134         mtspr( dec, val );              /* Set inital val */
135 #else
136         set_pit(gd->bd->bi_intfreq / 1000);
137 #endif
138 #endif  /* CONFIG_4xx */
139
140 #ifdef CONFIG_ADCIOP
141         /*
142          * Init PIT
143          */
144         set_pit(66000);
145 #endif
146
147         /*
148          * Enable PIT
149          */
150         val = mfspr(tcr);
151         val |= 0x04400000;
152         mtspr(tcr, val);
153
154         /*
155          * Set EVPR to 0
156          */
157         set_evpr(0x00000000);
158
159 #if !defined(CONFIG_440GX)
160 #if (UIC_MAX > 1)
161         /* Install the UIC1 handlers */
162         irq_install_handler(VECNUM_UIC1NC, uic_cascade_interrupt, 0);
163         irq_install_handler(VECNUM_UIC1C, uic_cascade_interrupt, 0);
164 #endif
165 #if (UIC_MAX > 2)
166         irq_install_handler(VECNUM_UIC2NC, uic_cascade_interrupt, 0);
167         irq_install_handler(VECNUM_UIC2C, uic_cascade_interrupt, 0);
168 #endif
169 #if (UIC_MAX > 3)
170         irq_install_handler(VECNUM_UIC3NC, uic_cascade_interrupt, 0);
171         irq_install_handler(VECNUM_UIC3C, uic_cascade_interrupt, 0);
172 #endif
173 #else /* !defined(CONFIG_440GX) */
174         /* Take the GX out of compatibility mode
175          * Travis Sawyer, 9 Mar 2004
176          * NOTE: 440gx user manual inconsistency here
177          *       Compatibility mode and Ethernet Clock select are not
178          *       correct in the manual
179          */
180         mfsdr(sdr_mfr, val);
181         val &= ~0x10000000;
182         mtsdr(sdr_mfr,val);
183
184         /* Enable UIC interrupts via UIC Base Enable Register */
185         mtdcr(uicb0sr, UICB0_ALL);
186         mtdcr(uicb0er, 0x54000000);
187         /* None are critical */
188         mtdcr(uicb0cr, 0);
189 #endif /* !defined(CONFIG_440GX) */
190
191         return (0);
192 }
193
194 /* Handler for UIC interrupt */
195 static void uic_interrupt(u32 uic_base, int vec_base)
196 {
197         u32 uic_msr;
198         u32 msr_shift;
199         int vec;
200
201         /*
202          * Read masked interrupt status register to determine interrupt source
203          */
204         uic_msr = get_dcr(uic_base + UIC_MSR);
205         msr_shift = uic_msr;
206         vec = vec_base;
207
208         while (msr_shift != 0) {
209                 if (msr_shift & 0x80000000) {
210                         /*
211                          * Increment irq counter (for debug purpose only)
212                          */
213                         irq_vecs[vec].count++;
214
215                         if (irq_vecs[vec].handler != NULL) {
216                                 /* call isr */
217                                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
218                         } else {
219                                 set_dcr(uic_base + UIC_ER,
220                                         get_dcr(uic_base + UIC_ER) &
221                                         ~(0x80000000 >> vec));
222                                 printf("Masking bogus interrupt vector %d"
223                                        " (UIC_BASE=0x%x)\n", vec, uic_base);
224                         }
225
226                         /*
227                          * After servicing the interrupt, we have to remove the status indicator.
228                          */
229                         set_dcr(uic_base + UIC_SR, (0x80000000 >> vec));
230                 }
231
232                 /*
233                  * Shift msr to next position and increment vector
234                  */
235                 msr_shift <<= 1;
236                 vec++;
237         }
238 }
239
240 #if (UIC_MAX > 1) && !defined(CONFIG_440GX)
241 static void uic_cascade_interrupt(void *para)
242 {
243         external_interrupt(para);
244 }
245 #endif
246
247 #if defined(CONFIG_440)
248 #if defined(CONFIG_440GX)
249 /* 440GX uses base uic register */
250 #define UIC_BMSR        uicb0msr
251 #define UIC_BSR         uicb0sr
252 #else
253 #define UIC_BMSR        uic0msr
254 #define UIC_BSR         uic0sr
255 #endif
256 #else /* CONFIG_440 */
257 #define UIC_BMSR        uicmsr
258 #define UIC_BSR         uicsr
259 #endif /* CONFIG_440 */
260
261 /*
262  * Handle external interrupts
263  */
264 void external_interrupt(struct pt_regs *regs)
265 {
266         u32 uic_msr;
267
268         /*
269          * Read masked interrupt status register to determine interrupt source
270          */
271         uic_msr = mfdcr(UIC_BMSR);
272
273 #if (UIC_MAX > 1)
274         if ((UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr))
275                 uic_interrupt(UIC1_DCR_BASE, 32);
276 #endif
277
278 #if (UIC_MAX > 2)
279         if ((UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr))
280                 uic_interrupt(UIC2_DCR_BASE, 64);
281 #endif
282
283 #if (UIC_MAX > 3)
284         if ((UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr))
285                 uic_interrupt(UIC3_DCR_BASE, 96);
286 #endif
287
288 #if defined(CONFIG_440)
289 #if !defined(CONFIG_440GX)
290         if (uic_msr & ~(UICB0_ALL))
291                 uic_interrupt(UIC0_DCR_BASE, 0);
292 #else
293         if ((UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr))
294                 uic_interrupt(UIC0_DCR_BASE, 0);
295 #endif
296 #else /* CONFIG_440 */
297         uic_interrupt(UIC0_DCR_BASE, 0);
298 #endif /* CONFIG_440 */
299
300         mtdcr(UIC_BSR, uic_msr);
301
302         return;
303 }
304
305 /*
306  * Install and free a interrupt handler.
307  */
308 void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
309 {
310         int i;
311
312         /*
313          * Print warning when replacing with a different irq vector
314          */
315         if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) {
316                 printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
317                        vec, (uint) handler, (uint) irq_vecs[vec].handler);
318         }
319         irq_vecs[vec].handler = handler;
320         irq_vecs[vec].arg = arg;
321
322         i = vec & 0x1f;
323         if ((vec >= 0) && (vec < 32))
324                 mtdcr(uicer, mfdcr(uicer) | (0x80000000 >> i));
325 #if (UIC_MAX > 1)
326         else if ((vec >= 32) && (vec < 64))
327                 mtdcr(uic1er, mfdcr(uic1er) | (0x80000000 >> i));
328 #endif
329 #if (UIC_MAX > 2)
330         else if ((vec >= 64) && (vec < 96))
331                 mtdcr(uic2er, mfdcr(uic2er) | (0x80000000 >> i));
332 #endif
333 #if (UIC_MAX > 3)
334         else if (vec >= 96)
335                 mtdcr(uic3er, mfdcr(uic3er) | (0x80000000 >> i));
336 #endif
337
338         debug("Install interrupt for vector %d ==> %p\n", vec, handler);
339 }
340
341 void irq_free_handler (int vec)
342 {
343         int i;
344
345         debug("Free interrupt for vector %d ==> %p\n",
346               vec, irq_vecs[vec].handler);
347
348         i = vec & 0x1f;
349         if ((vec >= 0) && (vec < 32))
350                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> i));
351 #if (UIC_MAX > 1)
352         else if ((vec >= 32) && (vec < 64))
353                 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> i));
354 #endif
355 #if (UIC_MAX > 2)
356         else if ((vec >= 64) && (vec < 96))
357                 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> i));
358 #endif
359 #if (UIC_MAX > 3)
360         else if (vec >= 96)
361                 mtdcr(uic3er, mfdcr(uic3er) & ~(0x80000000 >> i));
362 #endif
363
364         irq_vecs[vec].handler = NULL;
365         irq_vecs[vec].arg = NULL;
366 }
367
368 void timer_interrupt_cpu (struct pt_regs *regs)
369 {
370         /* nothing to do here */
371         return;
372 }
373
374 #if defined(CONFIG_CMD_IRQ)
375 int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
376 {
377         int vec;
378
379         printf ("Interrupt-Information:\n");
380         printf ("Nr  Routine   Arg       Count\n");
381
382         for (vec = 0; vec < (UIC_MAX * 32); vec++) {
383                 if (irq_vecs[vec].handler != NULL) {
384                         printf ("%02d  %08lx  %08lx  %d\n",
385                                 vec,
386                                 (ulong)irq_vecs[vec].handler,
387                                 (ulong)irq_vecs[vec].arg,
388                                 irq_vecs[vec].count);
389                 }
390         }
391
392         return 0;
393 }
394 #endif