]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - cpu/mpc8260/interrupts.c
d80440891f90c3354fd73f8bc0144627c9c90e84
[karo-tx-uboot.git] / cpu / mpc8260 / interrupts.c
1 /*
2  * (C) Copyright 2000-2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  *
23  * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 22-Oct-00
24  */
25
26 #include <common.h>
27 #include <watchdog.h>
28 #include <command.h>
29 #include <mpc8260.h>
30 #include <mpc8260_irq.h>
31 #include <asm/processor.h>
32
33 /****************************************************************************/
34
35 unsigned decrementer_count;             /* count val for 1e6/HZ microseconds */
36
37 struct irq_action {
38         interrupt_handler_t *handler;
39         void *arg;
40         ulong count;
41 };
42
43 static struct irq_action irq_handlers[NR_IRQS];
44
45 static ulong ppc_cached_irq_mask[NR_MASK_WORDS];
46
47 /****************************************************************************/
48 /* this section was ripped out of arch/ppc/kernel/ppc8260_pic.c in the      */
49 /* Linux/PPC 2.4.x source. There was no copyright notice in that file.      */
50
51 /* The 8260 internal interrupt controller.  It is usually
52  * the only interrupt controller.
53  * There are two 32-bit registers (high/low) for up to 64
54  * possible interrupts.
55  *
56  * Now, the fun starts.....Interrupt Numbers DO NOT MAP
57  * in a simple arithmetic fashion to mask or pending registers.
58  * That is, interrupt 4 does not map to bit position 4.
59  * We create two tables, indexed by vector number, to indicate
60  * which register to use and which bit in the register to use.
61  */
62 static u_char irq_to_siureg[] = {
63         1, 1, 1, 1, 1, 1, 1, 1,
64         1, 1, 1, 1, 1, 1, 1, 1,
65         0, 0, 0, 0, 0, 0, 0, 0,
66         0, 0, 0, 0, 0, 0, 0, 0,
67         1, 1, 1, 1, 1, 1, 1, 1,
68         1, 1, 1, 1, 1, 1, 1, 1,
69         0, 0, 0, 0, 0, 0, 0, 0,
70         0, 0, 0, 0, 0, 0, 0, 0
71 };
72
73 static u_char irq_to_siubit[] = {
74         31, 16, 17, 18, 19, 20, 21, 22,
75         23, 24, 25, 26, 27, 28, 29, 30,
76         29, 30, 16, 17, 18, 19, 20, 21,
77         22, 23, 24, 25, 26, 27, 28, 31,
78         0, 1, 2, 3, 4, 5, 6, 7,
79         8, 9, 10, 11, 12, 13, 14, 15,
80         15, 14, 13, 12, 11, 10, 9, 8,
81         7, 6, 5, 4, 3, 2, 1, 0
82 };
83
84 static void m8260_mask_irq (unsigned int irq_nr)
85 {
86         volatile immap_t *immr = (immap_t *) CFG_IMMR;
87         int bit, word;
88         volatile uint *simr;
89
90         bit = irq_to_siubit[irq_nr];
91         word = irq_to_siureg[irq_nr];
92
93         simr = &(immr->im_intctl.ic_simrh);
94         ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
95         simr[word] = ppc_cached_irq_mask[word];
96 }
97
98 static void m8260_unmask_irq (unsigned int irq_nr)
99 {
100         volatile immap_t *immr = (immap_t *) CFG_IMMR;
101         int bit, word;
102         volatile uint *simr;
103
104         bit = irq_to_siubit[irq_nr];
105         word = irq_to_siureg[irq_nr];
106
107         simr = &(immr->im_intctl.ic_simrh);
108         ppc_cached_irq_mask[word] |= (1 << (31 - bit));
109         simr[word] = ppc_cached_irq_mask[word];
110 }
111
112 static void m8260_mask_and_ack (unsigned int irq_nr)
113 {
114         volatile immap_t *immr = (immap_t *) CFG_IMMR;
115         int bit, word;
116         volatile uint *simr, *sipnr;
117
118         bit = irq_to_siubit[irq_nr];
119         word = irq_to_siureg[irq_nr];
120
121         simr = &(immr->im_intctl.ic_simrh);
122         sipnr = &(immr->im_intctl.ic_sipnrh);
123         ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
124         simr[word] = ppc_cached_irq_mask[word];
125         sipnr[word] = 1 << (31 - bit);
126 }
127
128 static int m8260_get_irq (struct pt_regs *regs)
129 {
130         volatile immap_t *immr = (immap_t *) CFG_IMMR;
131         int irq;
132         unsigned long bits;
133
134         /* For MPC8260, read the SIVEC register and shift the bits down
135          * to get the irq number.         */
136         bits = immr->im_intctl.ic_sivec;
137         irq = bits >> 26;
138         return irq;
139 }
140
141 /* end of code ripped out of arch/ppc/kernel/ppc8260_pic.c                  */
142 /****************************************************************************/
143
144 static __inline__ unsigned long get_msr (void)
145 {
146         unsigned long msr;
147
148         __asm__ __volatile__ ("mfmsr %0":"=r" (msr):);
149
150         return msr;
151 }
152
153 static __inline__ void set_msr (unsigned long msr)
154 {
155         __asm__ __volatile__ ("mtmsr %0"::"r" (msr));
156 }
157
158 static __inline__ unsigned long get_dec (void)
159 {
160         unsigned long val;
161
162         __asm__ __volatile__ ("mfdec %0":"=r" (val):);
163
164         return val;
165 }
166
167 static __inline__ void set_dec (unsigned long val)
168 {
169         __asm__ __volatile__ ("mtdec %0"::"r" (val));
170 }
171
172 void enable_interrupts (void)
173 {
174         set_msr (get_msr () | MSR_EE);
175 }
176
177 /* returns flag if MSR_EE was set before */
178 int disable_interrupts (void)
179 {
180         ulong msr = get_msr ();
181
182         set_msr (msr & ~MSR_EE);
183         return ((msr & MSR_EE) != 0);
184 }
185
186 /****************************************************************************/
187
188 int interrupt_init (void)
189 {
190         DECLARE_GLOBAL_DATA_PTR;
191
192         volatile immap_t *immr = (immap_t *) CFG_IMMR;
193
194         decrementer_count = (gd->bus_clk / 4) / CFG_HZ;
195
196         /* Initialize the default interrupt mapping priorities */
197         immr->im_intctl.ic_sicr = 0;
198         immr->im_intctl.ic_siprr = 0x05309770;
199         immr->im_intctl.ic_scprrh = 0x05309770;
200         immr->im_intctl.ic_scprrl = 0x05309770;
201
202         /* disable all interrupts and clear all pending bits */
203         immr->im_intctl.ic_simrh = ppc_cached_irq_mask[0] = 0;
204         immr->im_intctl.ic_simrl = ppc_cached_irq_mask[1] = 0;
205         immr->im_intctl.ic_sipnrh = 0xffffffff;
206         immr->im_intctl.ic_sipnrl = 0xffffffff;
207
208         set_dec (decrementer_count);
209
210         set_msr (get_msr () | MSR_EE);
211
212         return (0);
213 }
214
215 /****************************************************************************/
216
217 /*
218  * Handle external interrupts
219  */
220 void external_interrupt (struct pt_regs *regs)
221 {
222         int irq, unmask = 1;
223
224         irq = m8260_get_irq (regs);
225
226         m8260_mask_and_ack (irq);
227
228         set_msr (get_msr () | MSR_EE);
229
230         if (irq_handlers[irq].handler != NULL)
231                 (*irq_handlers[irq].handler) (irq_handlers[irq].arg);
232         else {
233                 printf ("\nBogus External Interrupt IRQ %d\n", irq);
234                 /*
235                  * turn off the bogus interrupt, otherwise it
236                  * might repeat forever
237                  */
238                 unmask = 0;
239         }
240
241         if (unmask)
242                 m8260_unmask_irq (irq);
243 }
244
245 /****************************************************************************/
246
247 /*
248  * Install and free an interrupt handler.
249  */
250
251 void
252 irq_install_handler (int irq, interrupt_handler_t * handler, void *arg)
253 {
254         if (irq < 0 || irq >= NR_IRQS) {
255                 printf ("irq_install_handler: bad irq number %d\n", irq);
256                 return;
257         }
258
259         if (irq_handlers[irq].handler != NULL)
260                 printf ("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
261                                 (ulong) handler, (ulong) irq_handlers[irq].handler);
262
263         irq_handlers[irq].handler = handler;
264         irq_handlers[irq].arg = arg;
265
266         m8260_unmask_irq (irq);
267 }
268
269 void irq_free_handler (int irq)
270 {
271         if (irq < 0 || irq >= NR_IRQS) {
272                 printf ("irq_free_handler: bad irq number %d\n", irq);
273                 return;
274         }
275
276         m8260_mask_irq (irq);
277
278         irq_handlers[irq].handler = NULL;
279         irq_handlers[irq].arg = NULL;
280 }
281
282 /****************************************************************************/
283
284 volatile ulong timestamp = 0;
285
286 /*
287  * timer_interrupt - gets called when the decrementer overflows,
288  * with interrupts disabled.
289  * Trivial implementation - no need to be really accurate.
290  */
291 void timer_interrupt (struct pt_regs *regs)
292 {
293 #if defined(CONFIG_WATCHDOG) || defined(CFG_HYMOD_DBLEDS)
294         volatile immap_t *immr = (immap_t *) CFG_IMMR;
295 #endif                                                  /* CONFIG_WATCHDOG */
296
297         /* Restore Decrementer Count */
298         set_dec (decrementer_count);
299
300         timestamp++;
301
302 #if defined(CONFIG_WATCHDOG) || \
303     defined(CFG_CMA_LCD_HEARTBEAT) || \
304     defined(CFG_HYMOD_DBLEDS)
305
306         if ((timestamp % CFG_HZ) == 0) {
307 #if defined(CFG_CMA_LCD_HEARTBEAT)
308                 extern void lcd_heartbeat (void);
309 #endif                                                  /* CFG_CMA_LCD_HEARTBEAT */
310 #if defined(CFG_HYMOD_DBLEDS)
311                 volatile iop8260_t *iop = &immr->im_ioport;
312                 static int shift = 0;
313 #endif                                                  /* CFG_HYMOD_DBLEDS */
314
315 #if defined(CFG_CMA_LCD_HEARTBEAT)
316                 lcd_heartbeat ();
317 #endif                                                  /* CFG_CMA_LCD_HEARTBEAT */
318
319 #if defined(CONFIG_WATCHDOG)
320                 reset_8260_watchdog (immr);
321 #endif                                                  /* CONFIG_WATCHDOG */
322
323 #if defined(CFG_HYMOD_DBLEDS)
324                 /* hymod daughter board LEDs */
325                 if (++shift > 3)
326                         shift = 0;
327                 iop->iop_pdatd =
328                                 (iop->iop_pdatd & ~0x0f000000) | (1 << (24 + shift));
329 #endif                                                  /* CFG_HYMOD_DBLEDS */
330         }
331 #endif                                                  /* CONFIG_WATCHDOG || CFG_CMA_LCD_HEARTBEAT */
332 }
333
334 /****************************************************************************/
335
336 void reset_timer (void)
337 {
338         timestamp = 0;
339 }
340
341 ulong get_timer (ulong base)
342 {
343         return (timestamp - base);
344 }
345
346 void set_timer (ulong t)
347 {
348         timestamp = t;
349 }
350
351 /****************************************************************************/
352
353 #if (CONFIG_COMMANDS & CFG_CMD_IRQ)
354
355 /* ripped this out of ppc4xx/interrupts.c */
356
357 /*******************************************************************************
358 *
359 * irqinfo - print information about PCI devices
360 *
361 */
362 void
363 do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[])
364 {
365         int irq, re_enable;
366
367         re_enable = disable_interrupts ();
368
369         printf ("\nInterrupt-Information:\n");
370         printf ("Nr  Routine   Arg       Count\n");
371
372         for (irq = 0; irq < 32; irq++)
373                 if (irq_handlers[irq].handler != NULL)
374                         printf ("%02d  %08lx  %08lx  %ld\n", irq,
375                                         (ulong) irq_handlers[irq].handler,
376                                         (ulong) irq_handlers[irq].arg,
377                                         irq_handlers[irq].count);
378
379         if (re_enable)
380                 enable_interrupts ();
381 }
382
383 #endif                                                  /* CONFIG_COMMANDS & CFG_CMD_IRQ */