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 *
wdenk544e9732004-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 Fuchs0d548f92008-01-08 15:50:49 +010037#include <asm/ppc4xx-intvec.h>
wdenkf780aa22002-09-18 19:21:21 +000038
Wolfgang Denk6405a152006-03-31 18:32:53 +020039DECLARE_GLOBAL_DATA_PTR;
40
Stefan Roesee4790122008-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 Roesee4790122008-02-19 22:07:57 +010067static struct irq_action irq_vecs[UIC_MAX * 32];
wdenkf780aa22002-09-18 19:21:21 +000068
Stefan Roesee4790122008-02-19 22:07:57 +010069u32 get_dcr(u16);
70void set_dcr(u16, u32);
wdenkf780aa22002-09-18 19:21:21 +000071
Stefan Roesee4790122008-02-19 22:07:57 +010072#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
73static void uic_cascade_interrupt(void *para);
74#endif
wdenkf780aa22002-09-18 19:21:21 +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
wdenkc0aa5c52003-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
wdenkc0aa5c52003-12-06 19:49:23 +0000109 /* decrementer is automatically reloaded */
110 *decrementer_count = 0;
wdenk1ebf41e2004-01-02 14:00:00 +0000111
wdenkf780aa22002-09-18 19:21:21 +0000112 /*
113 * Mark all irqs as free
114 */
Stefan Roesee4790122008-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 */
stroese4b31de82005-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 Roeseb30f2a12005-08-08 12:42:22 +0200159#if !defined(CONFIG_440GX)
Stefan Roesee4790122008-02-19 22:07:57 +0100160#if (UIC_MAX > 1)
wdenkf780aa22002-09-18 19:21:21 +0000161 /* Install the UIC1 handlers */
Stefan Roesee4790122008-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 Roesee4790122008-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);
wdenk544e9732004-02-06 23:19:44 +0000168#endif
Stefan Roesee4790122008-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) */
wdenke28cf632004-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
wdenk544e9732004-02-06 23:19:44 +0000184 /* Enable UIC interrupts via UIC Base Enable Register */
wdenke28cf632004-03-14 15:20:55 +0000185 mtdcr(uicb0sr, UICB0_ALL);
186 mtdcr(uicb0er, 0x54000000);
187 /* None are critical */
188 mtdcr(uicb0cr, 0);
Stefan Roesee4790122008-02-19 22:07:57 +0100189#endif /* !defined(CONFIG_440GX) */
wdenkf780aa22002-09-18 19:21:21 +0000190
191 return (0);
192}
193
Stefan Roesee4790122008-02-19 22:07:57 +0100194/* Handler for UIC interrupt */
195static void uic_interrupt(u32 uic_base, int vec_base)
wdenk544e9732004-02-06 23:19:44 +0000196{
Stefan Roesee4790122008-02-19 22:07:57 +0100197 u32 uic_msr;
198 u32 msr_shift;
wdenkf780aa22002-09-18 19:21:21 +0000199 int vec;
200
201 /*
202 * Read masked interrupt status register to determine interrupt source
203 */
Stefan Roesee4790122008-02-19 22:07:57 +0100204 uic_msr = get_dcr(uic_base + UIC_MSR);
wdenkf780aa22002-09-18 19:21:21 +0000205 msr_shift = uic_msr;
Stefan Roesee4790122008-02-19 22:07:57 +0100206 vec = vec_base;
wdenkf780aa22002-09-18 19:21:21 +0000207
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 {
Stefan Roesee4790122008-02-19 22:07:57 +0100219 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);
wdenkf780aa22002-09-18 19:21:21 +0000224 }
225
226 /*
227 * After servicing the interrupt, we have to remove the status indicator.
228 */
Stefan Roesee4790122008-02-19 22:07:57 +0100229 set_dcr(uic_base + UIC_SR, (0x80000000 >> vec));
wdenkf780aa22002-09-18 19:21:21 +0000230 }
231
232 /*
233 * Shift msr to next position and increment vector
234 */
235 msr_shift <<= 1;
236 vec++;
237 }
238}
wdenk544e9732004-02-06 23:19:44 +0000239
Stefan Roesee4790122008-02-19 22:07:57 +0100240#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
241static void uic_cascade_interrupt(void *para)
wdenk544e9732004-02-06 23:19:44 +0000242{
Stefan Roesee4790122008-02-19 22:07:57 +0100243 external_interrupt(para);
wdenk544e9732004-02-06 23:19:44 +0000244}
Stefan Roesee4790122008-02-19 22:07:57 +0100245#endif
wdenkf780aa22002-09-18 19:21:21 +0000246
Stefan Roesee4790122008-02-19 22:07:57 +0100247#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
Stefan Roesee4790122008-02-19 22:07:57 +0100261/*
262 * Handle external interrupts
263 */
264void external_interrupt(struct pt_regs *regs)
wdenk544e9732004-02-06 23:19:44 +0000265{
Stefan Roesee4790122008-02-19 22:07:57 +0100266 u32 uic_msr;
wdenk544e9732004-02-06 23:19:44 +0000267
268 /*
269 * Read masked interrupt status register to determine interrupt source
270 */
Stefan Roesee4790122008-02-19 22:07:57 +0100271 uic_msr = mfdcr(UIC_BMSR);
wdenk544e9732004-02-06 23:19:44 +0000272
Stefan Roesee4790122008-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);
276#endif
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200277
Stefan Roesee4790122008-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
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200282
Stefan Roesee4790122008-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
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200287
Stefan Roesee4790122008-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 */
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200299
Stefan Roesee4790122008-02-19 22:07:57 +0100300 mtdcr(UIC_BSR, uic_msr);
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200301
Stefan Roesee4790122008-02-19 22:07:57 +0100302 return;
Marian Balakowicz49d0eee2006-06-30 16:30:46 +0200303}
wdenkf780aa22002-09-18 19:21:21 +0000304
305/*
306 * Install and free a interrupt handler.
307 */
Stefan Roesee4790122008-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 Roesee4790122008-02-19 22:07:57 +0100310 int i;
wdenkf780aa22002-09-18 19:21:21 +0000311
Stefan Roese326c9712005-08-01 16:41:48 +0200312 /*
Stefan Roesee4790122008-02-19 22:07:57 +0100313 * Print warning when replacing with a different irq vector
Stefan Roese326c9712005-08-01 16:41:48 +0200314 */
Stefan Roesee4790122008-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 Roesee4790122008-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 Roesee4790122008-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));
328#endif
329#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 Roesee4790122008-02-19 22:07:57 +0100333#if (UIC_MAX > 3)
334 else if (vec >= 96)
335 mtdcr(uic3er, mfdcr(uic3er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000336#endif
Stefan Roesee4790122008-02-19 22:07:57 +0100337
338 debug("Install interrupt for vector %d ==> %p\n", vec, handler);
wdenkf780aa22002-09-18 19:21:21 +0000339}
340
wdenk544e9732004-02-06 23:19:44 +0000341void irq_free_handler (int vec)
wdenkf780aa22002-09-18 19:21:21 +0000342{
Stefan Roesee4790122008-02-19 22:07:57 +0100343 int i;
wdenkf780aa22002-09-18 19:21:21 +0000344
Stefan Roesee4790122008-02-19 22:07:57 +0100345 debug("Free interrupt for vector %d ==> %p\n",
346 vec, irq_vecs[vec].handler);
wdenkf780aa22002-09-18 19:21:21 +0000347
Stefan Roesee4790122008-02-19 22:07:57 +0100348 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));
wdenkf780aa22002-09-18 19:21:21 +0000354#endif
Stefan Roesee4790122008-02-19 22:07:57 +0100355#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
wdenkf780aa22002-09-18 19:21:21 +0000363
Stefan Roesee4790122008-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
wdenkc0aa5c52003-12-06 19:49:23 +0000368void timer_interrupt_cpu (struct pt_regs *regs)
wdenkf780aa22002-09-18 19:21:21 +0000369{
wdenkc0aa5c52003-12-06 19:49:23 +0000370 /* nothing to do here */
371 return;
wdenkf780aa22002-09-18 19:21:21 +0000372}
373
Jon Loeligera5217742007-07-09 18:57:22 -0500374#if defined(CONFIG_CMD_IRQ)
Stefan Roesee4790122008-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 Roesee4790122008-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 Roesee4790122008-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
wdenk544e9732004-02-06 23:19:44 +0000392 return 0;
393}
Jon Loeligera5217742007-07-09 18:57:22 -0500394#endif