blob: 6dbd6d281907fa4fcd91483a53bedb276cec905c [file] [log] [blame]
wdenkf780aa22002-09-18 19:21:21 +00001/*
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 *
wdenkba56f622004-02-06 23:19:44 +00008 * (C) Copyright 2003 (440GX port)
9 * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com
10 *
wdenkf780aa22002-09-18 19:21:21 +000011 * 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>
wdenkf780aa22002-09-18 19:21:21 +000033#include <asm/processor.h>
34#include <ppc4xx.h>
35#include <ppc_asm.tmpl>
36#include <commproc.h>
wdenkf780aa22002-09-18 19:21:21 +000037
Stefan Roesed1631fe2008-06-26 13:40:57 +020038#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))
Stefan Roese56e41012008-02-19 22:07:57 +010047#else
Stefan Roesed1631fe2008-06-26 13:40:57 +020048#define UICB0_ALL (UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \
49 UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI))
Stefan Roese56e41012008-02-19 22:07:57 +010050#endif
Stefan Roesed1631fe2008-06-26 13:40:57 +020051#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
57DECLARE_GLOBAL_DATA_PTR;
wdenkf780aa22002-09-18 19:21:21 +000058
wdenkf780aa22002-09-18 19:21:21 +000059/*
60 * CPM interrupt vector functions.
61 */
62struct irq_action {
63 interrupt_handler_t *handler;
64 void *arg;
65 int count;
66};
67
Stefan Roese56e41012008-02-19 22:07:57 +010068static struct irq_action irq_vecs[UIC_MAX * 32];
wdenkf780aa22002-09-18 19:21:21 +000069
Stefan Roese56e41012008-02-19 22:07:57 +010070u32 get_dcr(u16);
71void set_dcr(u16, u32);
wdenkf780aa22002-09-18 19:21:21 +000072
Stefan Roese56e41012008-02-19 22:07:57 +010073#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
74static void uic_cascade_interrupt(void *para);
75#endif
wdenkba56f622004-02-06 23:19:44 +000076
wdenkf780aa22002-09-18 19:21:21 +000077#if defined(CONFIG_440)
78
79/* SPRN changed in 440 */
80static __inline__ void set_evpr(unsigned long val)
81{
82 asm volatile("mtspr 0x03f,%0" : : "r" (val));
83}
84
85#else /* !defined(CONFIG_440) */
86
wdenkf780aa22002-09-18 19:21:21 +000087static __inline__ void set_pit(unsigned long val)
88{
89 asm volatile("mtpit %0" : : "r" (val));
90}
91
92
93static __inline__ void set_tcr(unsigned long val)
94{
95 asm volatile("mttcr %0" : : "r" (val));
96}
97
98
99static __inline__ void set_evpr(unsigned long val)
100{
101 asm volatile("mtevpr %0" : : "r" (val));
102}
103#endif /* defined(CONFIG_440 */
104
wdenka8c7c702003-12-06 19:49:23 +0000105int interrupt_init_cpu (unsigned *decrementer_count)
wdenkf780aa22002-09-18 19:21:21 +0000106{
wdenkf780aa22002-09-18 19:21:21 +0000107 int vec;
108 unsigned long val;
109
wdenka8c7c702003-12-06 19:49:23 +0000110 /* decrementer is automatically reloaded */
111 *decrementer_count = 0;
wdenkd4ca31c2004-01-02 14:00:00 +0000112
wdenkf780aa22002-09-18 19:21:21 +0000113 /*
114 * Mark all irqs as free
115 */
Stefan Roese56e41012008-02-19 22:07:57 +0100116 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000117 irq_vecs[vec].handler = NULL;
118 irq_vecs[vec].arg = NULL;
119 irq_vecs[vec].count = 0;
wdenkf780aa22002-09-18 19:21:21 +0000120 }
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 */
stroese68e02362005-04-07 05:32:44 +0000133 val = gd->bd->bi_intfreq/1000; /* 1 msec */
wdenkf780aa22002-09-18 19:21:21 +0000134 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
Stefan Roese846b0dd2005-08-08 12:42:22 +0200160#if !defined(CONFIG_440GX)
Stefan Roese56e41012008-02-19 22:07:57 +0100161#if (UIC_MAX > 1)
wdenkf780aa22002-09-18 19:21:21 +0000162 /* Install the UIC1 handlers */
Stefan Roesed1631fe2008-06-26 13:40:57 +0200163 irq_install_handler(VECNUM_UIC1NCI, uic_cascade_interrupt, 0);
164 irq_install_handler(VECNUM_UIC1CI, uic_cascade_interrupt, 0);
wdenkf780aa22002-09-18 19:21:21 +0000165#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100166#if (UIC_MAX > 2)
Stefan Roesed1631fe2008-06-26 13:40:57 +0200167 irq_install_handler(VECNUM_UIC2NCI, uic_cascade_interrupt, 0);
168 irq_install_handler(VECNUM_UIC2CI, uic_cascade_interrupt, 0);
wdenkba56f622004-02-06 23:19:44 +0000169#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100170#if (UIC_MAX > 3)
Stefan Roesed1631fe2008-06-26 13:40:57 +0200171 irq_install_handler(VECNUM_UIC3NCI, uic_cascade_interrupt, 0);
172 irq_install_handler(VECNUM_UIC3CI, uic_cascade_interrupt, 0);
Stefan Roese56e41012008-02-19 22:07:57 +0100173#endif
174#else /* !defined(CONFIG_440GX) */
Stefan Roesed1631fe2008-06-26 13:40:57 +0200175 /*
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 */
wdenkaaf224a2004-03-14 15:20:55 +0000180 /* 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
wdenkba56f622004-02-06 23:19:44 +0000190 /* Enable UIC interrupts via UIC Base Enable Register */
wdenkaaf224a2004-03-14 15:20:55 +0000191 mtdcr(uicb0sr, UICB0_ALL);
Stefan Roesed1631fe2008-06-26 13:40:57 +0200192 mtdcr(uicb0er, UICB0_ALL);
wdenkaaf224a2004-03-14 15:20:55 +0000193 /* None are critical */
194 mtdcr(uicb0cr, 0);
Stefan Roese56e41012008-02-19 22:07:57 +0100195#endif /* !defined(CONFIG_440GX) */
wdenkf780aa22002-09-18 19:21:21 +0000196
197 return (0);
198}
199
Stefan Roese56e41012008-02-19 22:07:57 +0100200/* Handler for UIC interrupt */
201static 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,
Stefan Roesed1631fe2008-06-26 13:40:57 +0200226 get_dcr(uic_base + UIC_ER) & ~UIC_MASK(vec));
Stefan Roese56e41012008-02-19 22:07:57 +0100227 printf("Masking bogus interrupt vector %d"
228 " (UIC_BASE=0x%x)\n", vec, uic_base);
229 }
230
231 /*
Stefan Roesed8bd6432008-03-27 08:47:26 +0100232 * After servicing the interrupt, we have to remove the
233 * status indicator
Stefan Roese56e41012008-02-19 22:07:57 +0100234 */
Stefan Roesed1631fe2008-06-26 13:40:57 +0200235 set_dcr(uic_base + UIC_SR, UIC_MASK(vec));
Stefan Roese56e41012008-02-19 22:07:57 +0100236 }
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)
247static void uic_cascade_interrupt(void *para)
248{
249 external_interrupt(para);
250}
251#endif
252
Stefan Roese56e41012008-02-19 22:07:57 +0100253#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
wdenkf780aa22002-09-18 19:21:21 +0000261
262/*
263 * Handle external interrupts
264 */
wdenkba56f622004-02-06 23:19:44 +0000265void external_interrupt(struct pt_regs *regs)
266{
Stefan Roese56e41012008-02-19 22:07:57 +0100267 u32 uic_msr;
wdenkba56f622004-02-06 23:19:44 +0000268
269 /*
270 * Read masked interrupt status register to determine interrupt source
271 */
Stefan Roese56e41012008-02-19 22:07:57 +0100272 uic_msr = mfdcr(UIC_BMSR);
wdenkba56f622004-02-06 23:19:44 +0000273
Stefan Roese56e41012008-02-19 22:07:57 +0100274#if (UIC_MAX > 1)
Stefan Roesed1631fe2008-06-26 13:40:57 +0200275 if ((UIC_MASK(VECNUM_UIC1CI) & uic_msr) ||
276 (UIC_MASK(VECNUM_UIC1NCI) & uic_msr))
Stefan Roese56e41012008-02-19 22:07:57 +0100277 uic_interrupt(UIC1_DCR_BASE, 32);
wdenkba56f622004-02-06 23:19:44 +0000278#endif
279
Stefan Roese56e41012008-02-19 22:07:57 +0100280#if (UIC_MAX > 2)
Stefan Roesed1631fe2008-06-26 13:40:57 +0200281 if ((UIC_MASK(VECNUM_UIC2CI) & uic_msr) ||
282 (UIC_MASK(VECNUM_UIC2NCI) & uic_msr))
Stefan Roese56e41012008-02-19 22:07:57 +0100283 uic_interrupt(UIC2_DCR_BASE, 64);
284#endif
wdenkba56f622004-02-06 23:19:44 +0000285
Stefan Roese56e41012008-02-19 22:07:57 +0100286#if (UIC_MAX > 3)
Stefan Roesed1631fe2008-06-26 13:40:57 +0200287 if ((UIC_MASK(VECNUM_UIC3CI) & uic_msr) ||
288 (UIC_MASK(VECNUM_UIC3NCI) & uic_msr))
Stefan Roese56e41012008-02-19 22:07:57 +0100289 uic_interrupt(UIC3_DCR_BASE, 96);
290#endif
wdenkba56f622004-02-06 23:19:44 +0000291
Stefan Roese56e41012008-02-19 22:07:57 +0100292#if defined(CONFIG_440)
293#if !defined(CONFIG_440GX)
294 if (uic_msr & ~(UICB0_ALL))
295 uic_interrupt(UIC0_DCR_BASE, 0);
296#else
Stefan Roesed1631fe2008-06-26 13:40:57 +0200297 if ((UIC_MASK(VECNUM_UIC0CI) & uic_msr) ||
298 (UIC_MASK(VECNUM_UIC0NCI) & uic_msr))
Stefan Roese56e41012008-02-19 22:07:57 +0100299 uic_interrupt(UIC0_DCR_BASE, 0);
300#endif
301#else /* CONFIG_440 */
302 uic_interrupt(UIC0_DCR_BASE, 0);
303#endif /* CONFIG_440 */
wdenkba56f622004-02-06 23:19:44 +0000304
Stefan Roese56e41012008-02-19 22:07:57 +0100305 mtdcr(UIC_BSR, uic_msr);
wdenkba56f622004-02-06 23:19:44 +0000306
Stefan Roese56e41012008-02-19 22:07:57 +0100307 return;
wdenkba56f622004-02-06 23:19:44 +0000308}
309
wdenkf780aa22002-09-18 19:21:21 +0000310/*
311 * Install and free a interrupt handler.
312 */
Stefan Roese56e41012008-02-19 22:07:57 +0100313void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
wdenkf780aa22002-09-18 19:21:21 +0000314{
Stefan Roese56e41012008-02-19 22:07:57 +0100315 int i;
wdenkf780aa22002-09-18 19:21:21 +0000316
Stefan Roesec157d8e2005-08-01 16:41:48 +0200317 /*
Stefan Roese56e41012008-02-19 22:07:57 +0100318 * Print warning when replacing with a different irq vector
Stefan Roesec157d8e2005-08-01 16:41:48 +0200319 */
Stefan Roese56e41012008-02-19 22:07:57 +0100320 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);
wdenkf780aa22002-09-18 19:21:21 +0000323 }
Stefan Roese56e41012008-02-19 22:07:57 +0100324 irq_vecs[vec].handler = handler;
325 irq_vecs[vec].arg = arg;
wdenkf780aa22002-09-18 19:21:21 +0000326
Stefan Roese56e41012008-02-19 22:07:57 +0100327 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));
wdenkf780aa22002-09-18 19:21:21 +0000333#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100334#if (UIC_MAX > 2)
335 else if ((vec >= 64) && (vec < 96))
336 mtdcr(uic2er, mfdcr(uic2er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000337#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100338#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);
wdenkf780aa22002-09-18 19:21:21 +0000344}
345
wdenkba56f622004-02-06 23:19:44 +0000346void irq_free_handler (int vec)
wdenkf780aa22002-09-18 19:21:21 +0000347{
Stefan Roese56e41012008-02-19 22:07:57 +0100348 int i;
wdenkf780aa22002-09-18 19:21:21 +0000349
Stefan Roese56e41012008-02-19 22:07:57 +0100350 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));
wdenkf780aa22002-09-18 19:21:21 +0000367#endif
368
Stefan Roese56e41012008-02-19 22:07:57 +0100369 irq_vecs[vec].handler = NULL;
370 irq_vecs[vec].arg = NULL;
wdenkf780aa22002-09-18 19:21:21 +0000371}
372
wdenka8c7c702003-12-06 19:49:23 +0000373void timer_interrupt_cpu (struct pt_regs *regs)
wdenkf780aa22002-09-18 19:21:21 +0000374{
wdenka8c7c702003-12-06 19:49:23 +0000375 /* nothing to do here */
376 return;
wdenkf780aa22002-09-18 19:21:21 +0000377}
378
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500379#if defined(CONFIG_CMD_IRQ)
Stefan Roese56e41012008-02-19 22:07:57 +0100380int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
wdenkf780aa22002-09-18 19:21:21 +0000381{
382 int vec;
383
Stefan Roese56e41012008-02-19 22:07:57 +0100384 printf ("Interrupt-Information:\n");
wdenkf780aa22002-09-18 19:21:21 +0000385 printf ("Nr Routine Arg Count\n");
386
Stefan Roese56e41012008-02-19 22:07:57 +0100387 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000388 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
wdenkf780aa22002-09-18 19:21:21 +0000397 return 0;
398}
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500399#endif