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