8215dc652edb1d0fe5fb947ac547d558173bb53c
[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 #define UICB0_ALL       (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \
44                          UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI))
45 #elif (UIC_MAX > 1)
46 #define UICB0_ALL       (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI))
47 #else
48 #define UICB0_ALL       0
49 #endif
50
51 DECLARE_GLOBAL_DATA_PTR;
52
53 /*
54  * CPM interrupt vector functions.
55  */
56 struct  irq_action {
57         interrupt_handler_t *handler;
58         void *arg;
59         int count;
60 };
61
62 static struct irq_action irq_vecs[UIC_MAX * 32];
63
64 u32 get_dcr(u16);
65 void set_dcr(u16, u32);
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 int interrupt_init_cpu (unsigned *decrementer_count)
96 {
97         int vec;
98         unsigned long val;
99
100         /* decrementer is automatically reloaded */
101         *decrementer_count = 0;
102
103         /*
104          * Mark all irqs as free
105          */
106         for (vec = 0; vec < (UIC_MAX * 32); vec++) {
107                 irq_vecs[vec].handler = NULL;
108                 irq_vecs[vec].arg = NULL;
109                 irq_vecs[vec].count = 0;
110         }
111
112 #ifdef CONFIG_4xx
113         /*
114          * Init PIT
115          */
116 #if defined(CONFIG_440)
117         val = mfspr( tcr );
118         val &= (~0x04400000);           /* clear DIS & ARE */
119         mtspr( tcr, val );
120         mtspr( dec, 0 );                /* Prevent exception after TSR clear*/
121         mtspr( decar, 0 );              /* clear reload */
122         mtspr( tsr, 0x08000000 );       /* clear DEC status */
123         val = gd->bd->bi_intfreq/1000;  /* 1 msec */
124         mtspr( decar, val );            /* Set auto-reload value */
125         mtspr( dec, val );              /* Set inital val */
126 #else
127         set_pit(gd->bd->bi_intfreq / 1000);
128 #endif
129 #endif  /* CONFIG_4xx */
130
131 #ifdef CONFIG_ADCIOP
132         /*
133          * Init PIT
134          */
135         set_pit(66000);
136 #endif
137
138         /*
139          * Enable PIT
140          */
141         val = mfspr(tcr);
142         val |= 0x04400000;
143         mtspr(tcr, val);
144
145         /*
146          * Set EVPR to 0
147          */
148         set_evpr(0x00000000);
149
150 #if (UIC_MAX > 1)
151         /* Install the UIC1 handlers */
152         irq_install_handler(VECNUM_UIC1NCI, (void *)(void *)external_interrupt, 0);
153         irq_install_handler(VECNUM_UIC1CI, (void *)(void *)external_interrupt, 0);
154 #endif
155 #if (UIC_MAX > 2)
156         irq_install_handler(VECNUM_UIC2NCI, (void *)(void *)external_interrupt, 0);
157         irq_install_handler(VECNUM_UIC2CI, (void *)(void *)external_interrupt, 0);
158 #endif
159 #if (UIC_MAX > 3)
160         irq_install_handler(VECNUM_UIC3NCI, (void *)(void *)external_interrupt, 0);
161         irq_install_handler(VECNUM_UIC3CI, (void *)(void *)external_interrupt, 0);
162 #endif
163
164         return (0);
165 }
166
167 /* Handler for UIC interrupt */
168 static void uic_interrupt(u32 uic_base, int vec_base)
169 {
170         u32 uic_msr;
171         u32 msr_shift;
172         int vec;
173
174         /*
175          * Read masked interrupt status register to determine interrupt source
176          */
177         uic_msr = get_dcr(uic_base + UIC_MSR);
178         msr_shift = uic_msr;
179         vec = vec_base;
180
181         while (msr_shift != 0) {
182                 if (msr_shift & 0x80000000) {
183                         /*
184                          * Increment irq counter (for debug purpose only)
185                          */
186                         irq_vecs[vec].count++;
187
188                         if (irq_vecs[vec].handler != NULL) {
189                                 /* call isr */
190                                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
191                         } else {
192                                 set_dcr(uic_base + UIC_ER,
193                                         get_dcr(uic_base + UIC_ER) & ~UIC_MASK(vec));
194                                 printf("Masking bogus interrupt vector %d"
195                                        " (UIC_BASE=0x%x)\n", vec, uic_base);
196                         }
197
198                         /*
199                          * After servicing the interrupt, we have to remove the
200                          * status indicator
201                          */
202                         set_dcr(uic_base + UIC_SR, UIC_MASK(vec));
203                 }
204
205                 /*
206                  * Shift msr to next position and increment vector
207                  */
208                 msr_shift <<= 1;
209                 vec++;
210         }
211 }
212
213 /*
214  * Handle external interrupts
215  */
216 void external_interrupt(struct pt_regs *regs)
217 {
218         u32 uic_msr;
219
220         /*
221          * Read masked interrupt status register to determine interrupt source
222          */
223         uic_msr = mfdcr(uic0msr);
224
225 #if (UIC_MAX > 1)
226         if ((UIC_MASK(VECNUM_UIC1CI) & uic_msr) ||
227             (UIC_MASK(VECNUM_UIC1NCI) & uic_msr))
228                 uic_interrupt(UIC1_DCR_BASE, 32);
229 #endif
230
231 #if (UIC_MAX > 2)
232         if ((UIC_MASK(VECNUM_UIC2CI) & uic_msr) ||
233             (UIC_MASK(VECNUM_UIC2NCI) & uic_msr))
234                 uic_interrupt(UIC2_DCR_BASE, 64);
235 #endif
236
237 #if (UIC_MAX > 3)
238         if ((UIC_MASK(VECNUM_UIC3CI) & uic_msr) ||
239             (UIC_MASK(VECNUM_UIC3NCI) & uic_msr))
240                 uic_interrupt(UIC3_DCR_BASE, 96);
241 #endif
242
243         if (uic_msr & ~(UICB0_ALL))
244                 uic_interrupt(UIC0_DCR_BASE, 0);
245
246         mtdcr(uic0sr, uic_msr);
247
248         return;
249 }
250
251 /*
252  * Install and free a interrupt handler.
253  */
254 void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
255 {
256         /*
257          * Print warning when replacing with a different irq vector
258          */
259         if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) {
260                 printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
261                        vec, (uint) handler, (uint) irq_vecs[vec].handler);
262         }
263         irq_vecs[vec].handler = handler;
264         irq_vecs[vec].arg = arg;
265
266         if ((vec >= 0) && (vec < 32))
267                 mtdcr(uicer, mfdcr(uicer) | UIC_MASK(vec));
268 #if (UIC_MAX > 1)
269         else if ((vec >= 32) && (vec < 64))
270                 mtdcr(uic1er, mfdcr(uic1er) | UIC_MASK(vec));
271 #endif
272 #if (UIC_MAX > 2)
273         else if ((vec >= 64) && (vec < 96))
274                 mtdcr(uic2er, mfdcr(uic2er) | UIC_MASK(vec));
275 #endif
276 #if (UIC_MAX > 3)
277         else if (vec >= 96)
278                 mtdcr(uic3er, mfdcr(uic3er) | UIC_MASK(vec));
279 #endif
280
281         debug("Install interrupt for vector %d ==> %p\n", vec, handler);
282 }
283
284 void irq_free_handler (int vec)
285 {
286         debug("Free interrupt for vector %d ==> %p\n",
287               vec, irq_vecs[vec].handler);
288
289         if ((vec >= 0) && (vec < 32))
290                 mtdcr(uicer, mfdcr(uicer) & ~UIC_MASK(vec));
291 #if (UIC_MAX > 1)
292         else if ((vec >= 32) && (vec < 64))
293                 mtdcr(uic1er, mfdcr(uic1er) & ~UIC_MASK(vec));
294 #endif
295 #if (UIC_MAX > 2)
296         else if ((vec >= 64) && (vec < 96))
297                 mtdcr(uic2er, mfdcr(uic2er) & ~UIC_MASK(vec));
298 #endif
299 #if (UIC_MAX > 3)
300         else if (vec >= 96)
301                 mtdcr(uic3er, mfdcr(uic3er) & ~UIC_MASK(vec));
302 #endif
303
304         irq_vecs[vec].handler = NULL;
305         irq_vecs[vec].arg = NULL;
306 }
307
308 void timer_interrupt_cpu (struct pt_regs *regs)
309 {
310         /* nothing to do here */
311         return;
312 }
313
314 #if defined(CONFIG_CMD_IRQ)
315 int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
316 {
317         int vec;
318
319         printf ("Interrupt-Information:\n");
320         printf ("Nr  Routine   Arg       Count\n");
321
322         for (vec = 0; vec < (UIC_MAX * 32); vec++) {
323                 if (irq_vecs[vec].handler != NULL) {
324                         printf ("%02d  %08lx  %08lx  %d\n",
325                                 vec,
326                                 (ulong)irq_vecs[vec].handler,
327                                 (ulong)irq_vecs[vec].arg,
328                                 irq_vecs[vec].count);
329                 }
330         }
331
332         return 0;
333 }
334 #endif