blob: 698bcb57d7ce9ae5ba9929dafb5ecd3c250672ad [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>
Matthias Fuchs6e9233d2008-01-08 15:50:49 +010037#include <asm/ppc4xx-intvec.h>
wdenkf780aa22002-09-18 19:21:21 +000038
Wolfgang Denkd87080b2006-03-31 18:32:53 +020039DECLARE_GLOBAL_DATA_PTR;
40
Stefan Roese56e41012008-02-19 22:07:57 +010041/*
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
wdenkf780aa22002-09-18 19:21:21 +000057
wdenkf780aa22002-09-18 19:21:21 +000058/*
59 * CPM interrupt vector functions.
60 */
61struct irq_action {
62 interrupt_handler_t *handler;
63 void *arg;
64 int count;
65};
66
Stefan Roese56e41012008-02-19 22:07:57 +010067static struct irq_action irq_vecs[UIC_MAX * 32];
wdenkf780aa22002-09-18 19:21:21 +000068
Stefan Roese56e41012008-02-19 22:07:57 +010069u32 get_dcr(u16);
70void set_dcr(u16, u32);
wdenkf780aa22002-09-18 19:21:21 +000071
Stefan Roese56e41012008-02-19 22:07:57 +010072#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
73static void uic_cascade_interrupt(void *para);
74#endif
wdenkba56f622004-02-06 23:19:44 +000075
wdenkf780aa22002-09-18 19:21:21 +000076#if defined(CONFIG_440)
77
78/* SPRN changed in 440 */
79static __inline__ void set_evpr(unsigned long val)
80{
81 asm volatile("mtspr 0x03f,%0" : : "r" (val));
82}
83
84#else /* !defined(CONFIG_440) */
85
wdenkf780aa22002-09-18 19:21:21 +000086static __inline__ void set_pit(unsigned long val)
87{
88 asm volatile("mtpit %0" : : "r" (val));
89}
90
91
92static __inline__ void set_tcr(unsigned long val)
93{
94 asm volatile("mttcr %0" : : "r" (val));
95}
96
97
98static __inline__ void set_evpr(unsigned long val)
99{
100 asm volatile("mtevpr %0" : : "r" (val));
101}
102#endif /* defined(CONFIG_440 */
103
wdenka8c7c702003-12-06 19:49:23 +0000104int interrupt_init_cpu (unsigned *decrementer_count)
wdenkf780aa22002-09-18 19:21:21 +0000105{
wdenkf780aa22002-09-18 19:21:21 +0000106 int vec;
107 unsigned long val;
108
wdenka8c7c702003-12-06 19:49:23 +0000109 /* decrementer is automatically reloaded */
110 *decrementer_count = 0;
wdenkd4ca31c2004-01-02 14:00:00 +0000111
wdenkf780aa22002-09-18 19:21:21 +0000112 /*
113 * Mark all irqs as free
114 */
Stefan Roese56e41012008-02-19 22:07:57 +0100115 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000116 irq_vecs[vec].handler = NULL;
117 irq_vecs[vec].arg = NULL;
118 irq_vecs[vec].count = 0;
wdenkf780aa22002-09-18 19:21:21 +0000119 }
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 */
stroese68e02362005-04-07 05:32:44 +0000132 val = gd->bd->bi_intfreq/1000; /* 1 msec */
wdenkf780aa22002-09-18 19:21:21 +0000133 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
Stefan Roese846b0dd2005-08-08 12:42:22 +0200159#if !defined(CONFIG_440GX)
Stefan Roese56e41012008-02-19 22:07:57 +0100160#if (UIC_MAX > 1)
wdenkf780aa22002-09-18 19:21:21 +0000161 /* Install the UIC1 handlers */
Stefan Roese56e41012008-02-19 22:07:57 +0100162 irq_install_handler(VECNUM_UIC1NC, uic_cascade_interrupt, 0);
163 irq_install_handler(VECNUM_UIC1C, uic_cascade_interrupt, 0);
wdenkf780aa22002-09-18 19:21:21 +0000164#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100165#if (UIC_MAX > 2)
166 irq_install_handler(VECNUM_UIC2NC, uic_cascade_interrupt, 0);
167 irq_install_handler(VECNUM_UIC2C, uic_cascade_interrupt, 0);
wdenkba56f622004-02-06 23:19:44 +0000168#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100169#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) */
wdenkaaf224a2004-03-14 15:20:55 +0000174 /* 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
wdenkba56f622004-02-06 23:19:44 +0000184 /* Enable UIC interrupts via UIC Base Enable Register */
wdenkaaf224a2004-03-14 15:20:55 +0000185 mtdcr(uicb0sr, UICB0_ALL);
186 mtdcr(uicb0er, 0x54000000);
187 /* None are critical */
188 mtdcr(uicb0cr, 0);
Stefan Roese56e41012008-02-19 22:07:57 +0100189#endif /* !defined(CONFIG_440GX) */
wdenkf780aa22002-09-18 19:21:21 +0000190
191 return (0);
192}
193
Stefan Roese56e41012008-02-19 22:07:57 +0100194/* Handler for UIC interrupt */
195static 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)
241static 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 */
wdenkf780aa22002-09-18 19:21:21 +0000260
261/*
262 * Handle external interrupts
263 */
wdenkba56f622004-02-06 23:19:44 +0000264void external_interrupt(struct pt_regs *regs)
265{
Stefan Roese56e41012008-02-19 22:07:57 +0100266 u32 uic_msr;
wdenkba56f622004-02-06 23:19:44 +0000267
268 /*
269 * Read masked interrupt status register to determine interrupt source
270 */
Stefan Roese56e41012008-02-19 22:07:57 +0100271 uic_msr = mfdcr(UIC_BMSR);
wdenkba56f622004-02-06 23:19:44 +0000272
Stefan Roese56e41012008-02-19 22:07:57 +0100273#if (UIC_MAX > 1)
274 if ((UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr))
275 uic_interrupt(UIC1_DCR_BASE, 32);
wdenkba56f622004-02-06 23:19:44 +0000276#endif
277
Stefan Roese56e41012008-02-19 22:07:57 +0100278#if (UIC_MAX > 2)
279 if ((UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr))
280 uic_interrupt(UIC2_DCR_BASE, 64);
281#endif
wdenkba56f622004-02-06 23:19:44 +0000282
Stefan Roese56e41012008-02-19 22:07:57 +0100283#if (UIC_MAX > 3)
284 if ((UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr))
285 uic_interrupt(UIC3_DCR_BASE, 96);
286#endif
wdenkba56f622004-02-06 23:19:44 +0000287
Stefan Roese56e41012008-02-19 22:07:57 +0100288#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 */
wdenkba56f622004-02-06 23:19:44 +0000299
Stefan Roese56e41012008-02-19 22:07:57 +0100300 mtdcr(UIC_BSR, uic_msr);
wdenkba56f622004-02-06 23:19:44 +0000301
Stefan Roese56e41012008-02-19 22:07:57 +0100302 return;
wdenkba56f622004-02-06 23:19:44 +0000303}
304
wdenkf780aa22002-09-18 19:21:21 +0000305/*
306 * Install and free a interrupt handler.
307 */
Stefan Roese56e41012008-02-19 22:07:57 +0100308void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
wdenkf780aa22002-09-18 19:21:21 +0000309{
Stefan Roese56e41012008-02-19 22:07:57 +0100310 int i;
wdenkf780aa22002-09-18 19:21:21 +0000311
Stefan Roesec157d8e2005-08-01 16:41:48 +0200312 /*
Stefan Roese56e41012008-02-19 22:07:57 +0100313 * Print warning when replacing with a different irq vector
Stefan Roesec157d8e2005-08-01 16:41:48 +0200314 */
Stefan Roese56e41012008-02-19 22:07:57 +0100315 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);
wdenkf780aa22002-09-18 19:21:21 +0000318 }
Stefan Roese56e41012008-02-19 22:07:57 +0100319 irq_vecs[vec].handler = handler;
320 irq_vecs[vec].arg = arg;
wdenkf780aa22002-09-18 19:21:21 +0000321
Stefan Roese56e41012008-02-19 22:07:57 +0100322 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));
wdenkf780aa22002-09-18 19:21:21 +0000328#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100329#if (UIC_MAX > 2)
330 else if ((vec >= 64) && (vec < 96))
331 mtdcr(uic2er, mfdcr(uic2er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000332#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100333#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);
wdenkf780aa22002-09-18 19:21:21 +0000339}
340
wdenkba56f622004-02-06 23:19:44 +0000341void irq_free_handler (int vec)
wdenkf780aa22002-09-18 19:21:21 +0000342{
Stefan Roese56e41012008-02-19 22:07:57 +0100343 int i;
wdenkf780aa22002-09-18 19:21:21 +0000344
Stefan Roese56e41012008-02-19 22:07:57 +0100345 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));
wdenkf780aa22002-09-18 19:21:21 +0000362#endif
363
Stefan Roese56e41012008-02-19 22:07:57 +0100364 irq_vecs[vec].handler = NULL;
365 irq_vecs[vec].arg = NULL;
wdenkf780aa22002-09-18 19:21:21 +0000366}
367
wdenka8c7c702003-12-06 19:49:23 +0000368void timer_interrupt_cpu (struct pt_regs *regs)
wdenkf780aa22002-09-18 19:21:21 +0000369{
wdenka8c7c702003-12-06 19:49:23 +0000370 /* nothing to do here */
371 return;
wdenkf780aa22002-09-18 19:21:21 +0000372}
373
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500374#if defined(CONFIG_CMD_IRQ)
Stefan Roese56e41012008-02-19 22:07:57 +0100375int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
wdenkf780aa22002-09-18 19:21:21 +0000376{
377 int vec;
378
Stefan Roese56e41012008-02-19 22:07:57 +0100379 printf ("Interrupt-Information:\n");
wdenkf780aa22002-09-18 19:21:21 +0000380 printf ("Nr Routine Arg Count\n");
381
Stefan Roese56e41012008-02-19 22:07:57 +0100382 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000383 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
wdenkf780aa22002-09-18 19:21:21 +0000392 return 0;
393}
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500394#endif