blob: eb9cb1d397b79c377fb231758e57f752f89bd1bb [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
Wolfgang Denkd87080b2006-03-31 18:32:53 +020038DECLARE_GLOBAL_DATA_PTR;
39
Stefan Roese56e41012008-02-19 22:07:57 +010040/*
41 * Define the number of UIC's
42 */
43#if defined(CONFIG_440SPE) || \
44 defined(CONFIG_460EX) || defined(CONFIG_460GT)
45#define UIC_MAX 4
46#elif defined(CONFIG_440GX) || \
47 defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
48 defined(CONFIG_405EX)
49#define UIC_MAX 3
50#elif defined(CONFIG_440GP) || defined(CONFIG_440SP) || \
51 defined(CONFIG_440EP) || defined(CONFIG_440GR)
52#define UIC_MAX 2
53#else
54#define UIC_MAX 1
55#endif
wdenkf780aa22002-09-18 19:21:21 +000056
wdenkf780aa22002-09-18 19:21:21 +000057/*
58 * CPM interrupt vector functions.
59 */
60struct irq_action {
61 interrupt_handler_t *handler;
62 void *arg;
63 int count;
64};
65
Stefan Roese56e41012008-02-19 22:07:57 +010066static struct irq_action irq_vecs[UIC_MAX * 32];
wdenkf780aa22002-09-18 19:21:21 +000067
Stefan Roese56e41012008-02-19 22:07:57 +010068u32 get_dcr(u16);
69void set_dcr(u16, u32);
wdenkf780aa22002-09-18 19:21:21 +000070
Stefan Roese56e41012008-02-19 22:07:57 +010071#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
72static void uic_cascade_interrupt(void *para);
73#endif
wdenkba56f622004-02-06 23:19:44 +000074
wdenkf780aa22002-09-18 19:21:21 +000075#if defined(CONFIG_440)
76
77/* SPRN changed in 440 */
78static __inline__ void set_evpr(unsigned long val)
79{
80 asm volatile("mtspr 0x03f,%0" : : "r" (val));
81}
82
83#else /* !defined(CONFIG_440) */
84
wdenkf780aa22002-09-18 19:21:21 +000085static __inline__ void set_pit(unsigned long val)
86{
87 asm volatile("mtpit %0" : : "r" (val));
88}
89
90
91static __inline__ void set_tcr(unsigned long val)
92{
93 asm volatile("mttcr %0" : : "r" (val));
94}
95
96
97static __inline__ void set_evpr(unsigned long val)
98{
99 asm volatile("mtevpr %0" : : "r" (val));
100}
101#endif /* defined(CONFIG_440 */
102
wdenka8c7c702003-12-06 19:49:23 +0000103int interrupt_init_cpu (unsigned *decrementer_count)
wdenkf780aa22002-09-18 19:21:21 +0000104{
wdenkf780aa22002-09-18 19:21:21 +0000105 int vec;
106 unsigned long val;
107
wdenka8c7c702003-12-06 19:49:23 +0000108 /* decrementer is automatically reloaded */
109 *decrementer_count = 0;
wdenkd4ca31c2004-01-02 14:00:00 +0000110
wdenkf780aa22002-09-18 19:21:21 +0000111 /*
112 * Mark all irqs as free
113 */
Stefan Roese56e41012008-02-19 22:07:57 +0100114 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000115 irq_vecs[vec].handler = NULL;
116 irq_vecs[vec].arg = NULL;
117 irq_vecs[vec].count = 0;
wdenkf780aa22002-09-18 19:21:21 +0000118 }
119
120#ifdef CONFIG_4xx
121 /*
122 * Init PIT
123 */
124#if defined(CONFIG_440)
125 val = mfspr( tcr );
126 val &= (~0x04400000); /* clear DIS & ARE */
127 mtspr( tcr, val );
128 mtspr( dec, 0 ); /* Prevent exception after TSR clear*/
129 mtspr( decar, 0 ); /* clear reload */
130 mtspr( tsr, 0x08000000 ); /* clear DEC status */
stroese68e02362005-04-07 05:32:44 +0000131 val = gd->bd->bi_intfreq/1000; /* 1 msec */
wdenkf780aa22002-09-18 19:21:21 +0000132 mtspr( decar, val ); /* Set auto-reload value */
133 mtspr( dec, val ); /* Set inital val */
134#else
135 set_pit(gd->bd->bi_intfreq / 1000);
136#endif
137#endif /* CONFIG_4xx */
138
139#ifdef CONFIG_ADCIOP
140 /*
141 * Init PIT
142 */
143 set_pit(66000);
144#endif
145
146 /*
147 * Enable PIT
148 */
149 val = mfspr(tcr);
150 val |= 0x04400000;
151 mtspr(tcr, val);
152
153 /*
154 * Set EVPR to 0
155 */
156 set_evpr(0x00000000);
157
Stefan Roese846b0dd2005-08-08 12:42:22 +0200158#if !defined(CONFIG_440GX)
Stefan Roese56e41012008-02-19 22:07:57 +0100159#if (UIC_MAX > 1)
wdenkf780aa22002-09-18 19:21:21 +0000160 /* Install the UIC1 handlers */
Stefan Roese56e41012008-02-19 22:07:57 +0100161 irq_install_handler(VECNUM_UIC1NC, uic_cascade_interrupt, 0);
162 irq_install_handler(VECNUM_UIC1C, uic_cascade_interrupt, 0);
wdenkf780aa22002-09-18 19:21:21 +0000163#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100164#if (UIC_MAX > 2)
165 irq_install_handler(VECNUM_UIC2NC, uic_cascade_interrupt, 0);
166 irq_install_handler(VECNUM_UIC2C, uic_cascade_interrupt, 0);
wdenkba56f622004-02-06 23:19:44 +0000167#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100168#if (UIC_MAX > 3)
169 irq_install_handler(VECNUM_UIC3NC, uic_cascade_interrupt, 0);
170 irq_install_handler(VECNUM_UIC3C, uic_cascade_interrupt, 0);
171#endif
172#else /* !defined(CONFIG_440GX) */
wdenkaaf224a2004-03-14 15:20:55 +0000173 /* Take the GX out of compatibility mode
174 * Travis Sawyer, 9 Mar 2004
175 * NOTE: 440gx user manual inconsistency here
176 * Compatibility mode and Ethernet Clock select are not
177 * correct in the manual
178 */
179 mfsdr(sdr_mfr, val);
180 val &= ~0x10000000;
181 mtsdr(sdr_mfr,val);
182
wdenkba56f622004-02-06 23:19:44 +0000183 /* Enable UIC interrupts via UIC Base Enable Register */
wdenkaaf224a2004-03-14 15:20:55 +0000184 mtdcr(uicb0sr, UICB0_ALL);
185 mtdcr(uicb0er, 0x54000000);
186 /* None are critical */
187 mtdcr(uicb0cr, 0);
Stefan Roese56e41012008-02-19 22:07:57 +0100188#endif /* !defined(CONFIG_440GX) */
wdenkf780aa22002-09-18 19:21:21 +0000189
190 return (0);
191}
192
Stefan Roese56e41012008-02-19 22:07:57 +0100193/* Handler for UIC interrupt */
194static void uic_interrupt(u32 uic_base, int vec_base)
195{
196 u32 uic_msr;
197 u32 msr_shift;
198 int vec;
199
200 /*
201 * Read masked interrupt status register to determine interrupt source
202 */
203 uic_msr = get_dcr(uic_base + UIC_MSR);
204 msr_shift = uic_msr;
205 vec = vec_base;
206
207 while (msr_shift != 0) {
208 if (msr_shift & 0x80000000) {
209 /*
210 * Increment irq counter (for debug purpose only)
211 */
212 irq_vecs[vec].count++;
213
214 if (irq_vecs[vec].handler != NULL) {
215 /* call isr */
216 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
217 } else {
218 set_dcr(uic_base + UIC_ER,
219 get_dcr(uic_base + UIC_ER) &
Stefan Roesed8bd6432008-03-27 08:47:26 +0100220 ~(0x80000000 >> (vec & 0x1f)));
Stefan Roese56e41012008-02-19 22:07:57 +0100221 printf("Masking bogus interrupt vector %d"
222 " (UIC_BASE=0x%x)\n", vec, uic_base);
223 }
224
225 /*
Stefan Roesed8bd6432008-03-27 08:47:26 +0100226 * After servicing the interrupt, we have to remove the
227 * status indicator
Stefan Roese56e41012008-02-19 22:07:57 +0100228 */
Stefan Roesed8bd6432008-03-27 08:47:26 +0100229 set_dcr(uic_base + UIC_SR, (0x80000000 >> (vec & 0x1f)));
Stefan Roese56e41012008-02-19 22:07:57 +0100230 }
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