socrates: Update NAND driver to new API.
[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 <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 & 0x1f)));
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
228                          * status indicator
229                          */
230                         set_dcr(uic_base + UIC_SR, (0x80000000 >> (vec & 0x1f)));
231                 }
232
233                 /*
234                  * Shift msr to next position and increment vector
235                  */
236                 msr_shift <<= 1;
237                 vec++;
238         }
239 }
240
241 #if (UIC_MAX > 1) && !defined(CONFIG_440GX)
242 static void uic_cascade_interrupt(void *para)
243 {
244         external_interrupt(para);
245 }
246 #endif
247
248 #if defined(CONFIG_440)
249 #if defined(CONFIG_440GX)
250 /* 440GX uses base uic register */
251 #define UIC_BMSR        uicb0msr
252 #define UIC_BSR         uicb0sr
253 #else
254 #define UIC_BMSR        uic0msr
255 #define UIC_BSR         uic0sr
256 #endif
257 #else /* CONFIG_440 */
258 #define UIC_BMSR        uicmsr
259 #define UIC_BSR         uicsr
260 #endif /* CONFIG_440 */
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 ((UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr))
276                 uic_interrupt(UIC1_DCR_BASE, 32);
277 #endif
278
279 #if (UIC_MAX > 2)
280         if ((UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr))
281                 uic_interrupt(UIC2_DCR_BASE, 64);
282 #endif
283
284 #if (UIC_MAX > 3)
285         if ((UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr))
286                 uic_interrupt(UIC3_DCR_BASE, 96);
287 #endif
288
289 #if defined(CONFIG_440)
290 #if !defined(CONFIG_440GX)
291         if (uic_msr & ~(UICB0_ALL))
292                 uic_interrupt(UIC0_DCR_BASE, 0);
293 #else
294         if ((UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr))
295                 uic_interrupt(UIC0_DCR_BASE, 0);
296 #endif
297 #else /* CONFIG_440 */
298         uic_interrupt(UIC0_DCR_BASE, 0);
299 #endif /* CONFIG_440 */
300
301         mtdcr(UIC_BSR, uic_msr);
302
303         return;
304 }
305
306 /*
307  * Install and free a interrupt handler.
308  */
309 void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
310 {
311         int i;
312
313         /*
314          * Print warning when replacing with a different irq vector
315          */
316         if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) {
317                 printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
318                        vec, (uint) handler, (uint) irq_vecs[vec].handler);
319         }
320         irq_vecs[vec].handler = handler;
321         irq_vecs[vec].arg = arg;
322
323         i = vec & 0x1f;
324         if ((vec >= 0) && (vec < 32))
325                 mtdcr(uicer, mfdcr(uicer) | (0x80000000 >> i));
326 #if (UIC_MAX > 1)
327         else if ((vec >= 32) && (vec < 64))
328                 mtdcr(uic1er, mfdcr(uic1er) | (0x80000000 >> i));
329 #endif
330 #if (UIC_MAX > 2)
331         else if ((vec >= 64) && (vec < 96))
332                 mtdcr(uic2er, mfdcr(uic2er) | (0x80000000 >> i));
333 #endif
334 #if (UIC_MAX > 3)
335         else if (vec >= 96)
336                 mtdcr(uic3er, mfdcr(uic3er) | (0x80000000 >> i));
337 #endif
338
339         debug("Install interrupt for vector %d ==> %p\n", vec, handler);
340 }
341
342 void irq_free_handler (int vec)
343 {
344         int i;
345
346         debug("Free interrupt for vector %d ==> %p\n",
347               vec, irq_vecs[vec].handler);
348
349         i = vec & 0x1f;
350         if ((vec >= 0) && (vec < 32))
351                 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> i));
352 #if (UIC_MAX > 1)
353         else if ((vec >= 32) && (vec < 64))
354                 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> i));
355 #endif
356 #if (UIC_MAX > 2)
357         else if ((vec >= 64) && (vec < 96))
358                 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> i));
359 #endif
360 #if (UIC_MAX > 3)
361         else if (vec >= 96)
362                 mtdcr(uic3er, mfdcr(uic3er) & ~(0x80000000 >> i));
363 #endif
364
365         irq_vecs[vec].handler = NULL;
366         irq_vecs[vec].arg = NULL;
367 }
368
369 void timer_interrupt_cpu (struct pt_regs *regs)
370 {
371         /* nothing to do here */
372         return;
373 }
374
375 #if defined(CONFIG_CMD_IRQ)
376 int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
377 {
378         int vec;
379
380         printf ("Interrupt-Information:\n");
381         printf ("Nr  Routine   Arg       Count\n");
382
383         for (vec = 0; vec < (UIC_MAX * 32); vec++) {
384                 if (irq_vecs[vec].handler != NULL) {
385                         printf ("%02d  %08lx  %08lx  %d\n",
386                                 vec,
387                                 (ulong)irq_vecs[vec].handler,
388                                 (ulong)irq_vecs[vec].arg,
389                                 irq_vecs[vec].count);
390                 }
391         }
392
393         return 0;
394 }
395 #endif