blob: 8620e2b48476da48b2ad97f905f1ef9cd562f2a7 [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) &
Stefan Roesed8bd6432008-03-27 08:47:26 +0100221 ~(0x80000000 >> (vec & 0x1f)));
Stefan Roese56e41012008-02-19 22:07:57 +0100222 printf("Masking bogus interrupt vector %d"
223 " (UIC_BASE=0x%x)\n", vec, uic_base);
224 }
225
226 /*
Stefan Roesed8bd6432008-03-27 08:47:26 +0100227 * After servicing the interrupt, we have to remove the
228 * status indicator
Stefan Roese56e41012008-02-19 22:07:57 +0100229 */
Stefan Roesed8bd6432008-03-27 08:47:26 +0100230 set_dcr(uic_base + UIC_SR, (0x80000000 >> (vec & 0x1f)));
Stefan Roese56e41012008-02-19 22:07:57 +0100231 }
232
233 /*
234 * Shift msr to next position and increment vector
235 */
236 msr_shift <<= 1;
237 vec++;
238 }
239}
240
241#if (UIC_MAX > 1) && !defined(CONFIG_440GX)
242static void uic_cascade_interrupt(void *para)
243{
244 external_interrupt(para);
245}
246#endif
247
248#if defined(CONFIG_440)
249#if defined(CONFIG_440GX)
250/* 440GX uses base uic register */
251#define UIC_BMSR uicb0msr
252#define UIC_BSR uicb0sr
253#else
254#define UIC_BMSR uic0msr
255#define UIC_BSR uic0sr
256#endif
257#else /* CONFIG_440 */
258#define UIC_BMSR uicmsr
259#define UIC_BSR uicsr
260#endif /* CONFIG_440 */
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)
275 if ((UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr))
276 uic_interrupt(UIC1_DCR_BASE, 32);
wdenkba56f622004-02-06 23:19:44 +0000277#endif
278
Stefan Roese56e41012008-02-19 22:07:57 +0100279#if (UIC_MAX > 2)
280 if ((UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr))
281 uic_interrupt(UIC2_DCR_BASE, 64);
282#endif
wdenkba56f622004-02-06 23:19:44 +0000283
Stefan Roese56e41012008-02-19 22:07:57 +0100284#if (UIC_MAX > 3)
285 if ((UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr))
286 uic_interrupt(UIC3_DCR_BASE, 96);
287#endif
wdenkba56f622004-02-06 23:19:44 +0000288
Stefan Roese56e41012008-02-19 22:07:57 +0100289#if defined(CONFIG_440)
290#if !defined(CONFIG_440GX)
291 if (uic_msr & ~(UICB0_ALL))
292 uic_interrupt(UIC0_DCR_BASE, 0);
293#else
294 if ((UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr))
295 uic_interrupt(UIC0_DCR_BASE, 0);
296#endif
297#else /* CONFIG_440 */
298 uic_interrupt(UIC0_DCR_BASE, 0);
299#endif /* CONFIG_440 */
wdenkba56f622004-02-06 23:19:44 +0000300
Stefan Roese56e41012008-02-19 22:07:57 +0100301 mtdcr(UIC_BSR, uic_msr);
wdenkba56f622004-02-06 23:19:44 +0000302
Stefan Roese56e41012008-02-19 22:07:57 +0100303 return;
wdenkba56f622004-02-06 23:19:44 +0000304}
305
wdenkf780aa22002-09-18 19:21:21 +0000306/*
307 * Install and free a interrupt handler.
308 */
Stefan Roese56e41012008-02-19 22:07:57 +0100309void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg)
wdenkf780aa22002-09-18 19:21:21 +0000310{
Stefan Roese56e41012008-02-19 22:07:57 +0100311 int i;
wdenkf780aa22002-09-18 19:21:21 +0000312
Stefan Roesec157d8e2005-08-01 16:41:48 +0200313 /*
Stefan Roese56e41012008-02-19 22:07:57 +0100314 * Print warning when replacing with a different irq vector
Stefan Roesec157d8e2005-08-01 16:41:48 +0200315 */
Stefan Roese56e41012008-02-19 22:07:57 +0100316 if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) {
317 printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
318 vec, (uint) handler, (uint) irq_vecs[vec].handler);
wdenkf780aa22002-09-18 19:21:21 +0000319 }
Stefan Roese56e41012008-02-19 22:07:57 +0100320 irq_vecs[vec].handler = handler;
321 irq_vecs[vec].arg = arg;
wdenkf780aa22002-09-18 19:21:21 +0000322
Stefan Roese56e41012008-02-19 22:07:57 +0100323 i = vec & 0x1f;
324 if ((vec >= 0) && (vec < 32))
325 mtdcr(uicer, mfdcr(uicer) | (0x80000000 >> i));
326#if (UIC_MAX > 1)
327 else if ((vec >= 32) && (vec < 64))
328 mtdcr(uic1er, mfdcr(uic1er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000329#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100330#if (UIC_MAX > 2)
331 else if ((vec >= 64) && (vec < 96))
332 mtdcr(uic2er, mfdcr(uic2er) | (0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000333#endif
Stefan Roese56e41012008-02-19 22:07:57 +0100334#if (UIC_MAX > 3)
335 else if (vec >= 96)
336 mtdcr(uic3er, mfdcr(uic3er) | (0x80000000 >> i));
337#endif
338
339 debug("Install interrupt for vector %d ==> %p\n", vec, handler);
wdenkf780aa22002-09-18 19:21:21 +0000340}
341
wdenkba56f622004-02-06 23:19:44 +0000342void irq_free_handler (int vec)
wdenkf780aa22002-09-18 19:21:21 +0000343{
Stefan Roese56e41012008-02-19 22:07:57 +0100344 int i;
wdenkf780aa22002-09-18 19:21:21 +0000345
Stefan Roese56e41012008-02-19 22:07:57 +0100346 debug("Free interrupt for vector %d ==> %p\n",
347 vec, irq_vecs[vec].handler);
348
349 i = vec & 0x1f;
350 if ((vec >= 0) && (vec < 32))
351 mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> i));
352#if (UIC_MAX > 1)
353 else if ((vec >= 32) && (vec < 64))
354 mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> i));
355#endif
356#if (UIC_MAX > 2)
357 else if ((vec >= 64) && (vec < 96))
358 mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> i));
359#endif
360#if (UIC_MAX > 3)
361 else if (vec >= 96)
362 mtdcr(uic3er, mfdcr(uic3er) & ~(0x80000000 >> i));
wdenkf780aa22002-09-18 19:21:21 +0000363#endif
364
Stefan Roese56e41012008-02-19 22:07:57 +0100365 irq_vecs[vec].handler = NULL;
366 irq_vecs[vec].arg = NULL;
wdenkf780aa22002-09-18 19:21:21 +0000367}
368
wdenka8c7c702003-12-06 19:49:23 +0000369void timer_interrupt_cpu (struct pt_regs *regs)
wdenkf780aa22002-09-18 19:21:21 +0000370{
wdenka8c7c702003-12-06 19:49:23 +0000371 /* nothing to do here */
372 return;
wdenkf780aa22002-09-18 19:21:21 +0000373}
374
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500375#if defined(CONFIG_CMD_IRQ)
Stefan Roese56e41012008-02-19 22:07:57 +0100376int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
wdenkf780aa22002-09-18 19:21:21 +0000377{
378 int vec;
379
Stefan Roese56e41012008-02-19 22:07:57 +0100380 printf ("Interrupt-Information:\n");
wdenkf780aa22002-09-18 19:21:21 +0000381 printf ("Nr Routine Arg Count\n");
382
Stefan Roese56e41012008-02-19 22:07:57 +0100383 for (vec = 0; vec < (UIC_MAX * 32); vec++) {
wdenkf780aa22002-09-18 19:21:21 +0000384 if (irq_vecs[vec].handler != NULL) {
385 printf ("%02d %08lx %08lx %d\n",
386 vec,
387 (ulong)irq_vecs[vec].handler,
388 (ulong)irq_vecs[vec].arg,
389 irq_vecs[vec].count);
390 }
391 }
392
wdenkf780aa22002-09-18 19:21:21 +0000393 return 0;
394}
Jon Loeliger3a1ed1e2007-07-09 18:57:22 -0500395#endif