]> Git Repo - u-boot.git/blame - cpu/mpc8xx/interrupts.c
* Patch by Martin Krause, 17 Jul 2003:
[u-boot.git] / cpu / mpc8xx / interrupts.c
CommitLineData
0de1ffc8
WD
1/*
2 * (C) Copyright 2000-2002
3 * Wolfgang Denk, DENX Software Engineering, [email protected].
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
24#include <common.h>
25#include <watchdog.h>
26#include <mpc8xx.h>
27#include <mpc8xx_irq.h>
28#include <asm/processor.h>
29#include <commproc.h>
30
7c7a23bd 31/************************************************************************/
0de1ffc8 32
7c7a23bd 33unsigned decrementer_count; /* count value for 1e6/HZ microseconds */
0de1ffc8 34
7c7a23bd 35/************************************************************************/
0de1ffc8
WD
36
37/*
38 * CPM interrupt vector functions.
39 */
7c7a23bd
WD
40struct interrupt_action {
41 interrupt_handler_t *handler;
42 void *arg;
0de1ffc8
WD
43};
44
7c7a23bd
WD
45static struct interrupt_action cpm_vecs[CPMVEC_NR];
46static struct interrupt_action irq_vecs[NR_IRQS];
0de1ffc8
WD
47
48static void cpm_interrupt_init (void);
7c7a23bd 49static void cpm_interrupt (void *regs);
0de1ffc8 50
7c7a23bd 51/************************************************************************/
0de1ffc8 52
7c7a23bd 53static __inline__ unsigned long get_msr (void)
0de1ffc8 54{
7c7a23bd 55 unsigned long msr;
0de1ffc8 56
7c7a23bd
WD
57 asm volatile ("mfmsr %0":"=r" (msr):);
58
59 return msr;
0de1ffc8
WD
60}
61
7c7a23bd 62static __inline__ void set_msr (unsigned long msr)
0de1ffc8 63{
7c7a23bd 64 asm volatile ("mtmsr %0"::"r" (msr));
0de1ffc8
WD
65}
66
7c7a23bd 67static __inline__ unsigned long get_dec (void)
0de1ffc8 68{
7c7a23bd
WD
69 unsigned long val;
70
71 asm volatile ("mfdec %0":"=r" (val):);
0de1ffc8 72
7c7a23bd 73 return val;
0de1ffc8
WD
74}
75
76
7c7a23bd 77static __inline__ void set_dec (unsigned long val)
0de1ffc8 78{
7c7a23bd 79 asm volatile ("mtdec %0"::"r" (val));
0de1ffc8
WD
80}
81
82
83void enable_interrupts (void)
84{
7c7a23bd 85 set_msr (get_msr () | MSR_EE);
0de1ffc8
WD
86}
87
88/* returns flag if MSR_EE was set before */
89int disable_interrupts (void)
90{
7c7a23bd
WD
91 ulong msr = get_msr ();
92
0de1ffc8
WD
93 set_msr (msr & ~MSR_EE);
94 return ((msr & MSR_EE) != 0);
95}
96
7c7a23bd 97/************************************************************************/
0de1ffc8 98
7c7a23bd 99int interrupt_init (void)
0de1ffc8 100{
7c7a23bd 101 volatile immap_t *immr = (immap_t *) CFG_IMMR;
0de1ffc8 102
7c7a23bd 103 decrementer_count = get_tbclk () / CFG_HZ;
0de1ffc8 104
7c7a23bd
WD
105 /* disable all interrupts */
106 immr->im_siu_conf.sc_simask = 0;
0de1ffc8 107
7c7a23bd
WD
108 /* Configure CPM interrupts */
109 cpm_interrupt_init ();
0de1ffc8
WD
110
111 set_dec (decrementer_count);
112
7c7a23bd 113 set_msr (get_msr () | MSR_EE);
0de1ffc8
WD
114
115 return (0);
116}
117
7c7a23bd 118/************************************************************************/
0de1ffc8
WD
119
120/*
121 * Handle external interrupts
122 */
7c7a23bd 123void external_interrupt (struct pt_regs *regs)
0de1ffc8 124{
7c7a23bd
WD
125 volatile immap_t *immr = (immap_t *) CFG_IMMR;
126 int irq;
127 ulong simask, newmask;
128 ulong vec, v_bit;
0de1ffc8
WD
129
130 /*
131 * read the SIVEC register and shift the bits down
132 * to get the irq number
133 */
134 vec = immr->im_siu_conf.sc_sivec;
135 irq = vec >> 26;
136 v_bit = 0x80000000UL >> irq;
137
138 /*
139 * Read Interrupt Mask Register and Mask Interrupts
140 */
141 simask = immr->im_siu_conf.sc_simask;
142 newmask = simask & (~(0xFFFF0000 >> irq));
143 immr->im_siu_conf.sc_simask = newmask;
144
7c7a23bd 145 if (!(irq & 0x1)) { /* External Interrupt ? */
0de1ffc8 146 ulong siel;
7c7a23bd 147
0de1ffc8
WD
148 /*
149 * Read Interrupt Edge/Level Register
150 */
151 siel = immr->im_siu_conf.sc_siel;
152
7c7a23bd 153 if (siel & v_bit) { /* edge triggered interrupt ? */
0de1ffc8
WD
154 /*
155 * Rewrite SIPEND Register to clear interrupt
156 */
157 immr->im_siu_conf.sc_sipend = v_bit;
158 }
159 }
160
7c7a23bd
WD
161 if (irq_vecs[irq].handler != NULL) {
162 irq_vecs[irq].handler (irq_vecs[irq].arg);
163 } else {
0de1ffc8 164 printf ("\nBogus External Interrupt IRQ %d Vector %ld\n",
7c7a23bd 165 irq, vec);
0de1ffc8
WD
166 /* turn off the bogus interrupt to avoid it from now */
167 simask &= ~v_bit;
0de1ffc8 168 }
0de1ffc8
WD
169 /*
170 * Re-Enable old Interrupt Mask
171 */
172 immr->im_siu_conf.sc_simask = simask;
173}
174
7c7a23bd 175/************************************************************************/
0de1ffc8
WD
176
177/*
178 * CPM interrupt handler
179 */
7c7a23bd 180static void cpm_interrupt (void *regs)
0de1ffc8 181{
7c7a23bd
WD
182 volatile immap_t *immr = (immap_t *) CFG_IMMR;
183 uint vec;
0de1ffc8
WD
184
185 /*
186 * Get the vector by setting the ACK bit
187 * and then reading the register.
188 */
189 immr->im_cpic.cpic_civr = 1;
190 vec = immr->im_cpic.cpic_civr;
191 vec >>= 11;
192
193 if (cpm_vecs[vec].handler != NULL) {
7c7a23bd 194 (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
0de1ffc8
WD
195 } else {
196 immr->im_cpic.cpic_cimr &= ~(1 << vec);
197 printf ("Masking bogus CPM interrupt vector 0x%x\n", vec);
198 }
199 /*
7c7a23bd
WD
200 * After servicing the interrupt,
201 * we have to remove the status indicator.
0de1ffc8
WD
202 */
203 immr->im_cpic.cpic_cisr |= (1 << vec);
204}
205
206/*
207 * The CPM can generate the error interrupt when there is a race
208 * condition between generating and masking interrupts. All we have
209 * to do is ACK it and return. This is a no-op function so we don't
210 * need any special tests in the interrupt handler.
211 */
7c7a23bd 212static void cpm_error_interrupt (void *dummy)
0de1ffc8
WD
213{
214}
215
7c7a23bd 216/************************************************************************/
0de1ffc8 217/*
7c7a23bd 218 * Install and free an interrupt handler
0de1ffc8 219 */
7c7a23bd
WD
220void irq_install_handler (int vec, interrupt_handler_t * handler,
221 void *arg)
0de1ffc8 222{
7c7a23bd
WD
223 volatile immap_t *immr = (immap_t *) CFG_IMMR;
224
225 if ((vec & CPMVEC_OFFSET) != 0) {
226 /* CPM interrupt */
227 vec &= 0xffff;
228 if (cpm_vecs[vec].handler != NULL) {
229 printf ("CPM interrupt 0x%x replacing 0x%x\n",
230 (uint) handler,
231 (uint) cpm_vecs[vec].handler);
232 }
233 cpm_vecs[vec].handler = handler;
234 cpm_vecs[vec].arg = arg;
235 immr->im_cpic.cpic_cimr |= (1 << vec);
236#if 0
237 printf ("Install CPM interrupt for vector %d ==> %p\n",
238 vec, handler);
239#endif
240 } else {
241 /* SIU interrupt */
242 if (irq_vecs[vec].handler != NULL) {
243 printf ("SIU interrupt %d 0x%x replacing 0x%x\n",
244 vec,
245 (uint) handler,
246 (uint) cpm_vecs[vec].handler);
247 }
248 irq_vecs[vec].handler = handler;
249 irq_vecs[vec].arg = arg;
250 immr->im_siu_conf.sc_simask |= 1 << (31 - vec);
0de1ffc8 251#if 0
7c7a23bd
WD
252 printf ("Install SIU interrupt for vector %d ==> %p\n",
253 vec, handler);
0de1ffc8 254#endif
7c7a23bd 255 }
0de1ffc8
WD
256}
257
7c7a23bd 258void irq_free_handler (int vec)
0de1ffc8 259{
7c7a23bd
WD
260 volatile immap_t *immr = (immap_t *) CFG_IMMR;
261
262 if ((vec & CPMVEC_OFFSET) != 0) {
263 /* CPM interrupt */
264 vec &= 0xffff;
265#if 0
266 printf ("Free CPM interrupt for vector %d ==> %p\n",
267 vec, cpm_vecs[vec].handler);
268#endif
269 immr->im_cpic.cpic_cimr &= ~(1 << vec);
270 cpm_vecs[vec].handler = NULL;
271 cpm_vecs[vec].arg = NULL;
272 } else {
273 /* SIU interrupt */
0de1ffc8 274#if 0
7c7a23bd
WD
275 printf ("Free CPM interrupt for vector %d ==> %p\n",
276 vec, cpm_vecs[vec].handler);
0de1ffc8 277#endif
7c7a23bd
WD
278 immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec));
279 irq_vecs[vec].handler = NULL;
280 irq_vecs[vec].arg = NULL;
281 }
0de1ffc8
WD
282}
283
7c7a23bd 284/************************************************************************/
0de1ffc8 285
7c7a23bd 286static void cpm_interrupt_init (void)
0de1ffc8 287{
7c7a23bd 288 volatile immap_t *immr = (immap_t *) CFG_IMMR;
0de1ffc8
WD
289
290 /*
291 * Initialize the CPM interrupt controller.
292 */
293
294 immr->im_cpic.cpic_cicr =
7c7a23bd
WD
295 (CICR_SCD_SCC4 |
296 CICR_SCC_SCC3 |
297 CICR_SCB_SCC2 |
298 CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
0de1ffc8
WD
299
300 immr->im_cpic.cpic_cimr = 0;
301
302 /*
303 * Install the error handler.
304 */
7c7a23bd 305 irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL);
0de1ffc8
WD
306
307 immr->im_cpic.cpic_cicr |= CICR_IEN;
7c7a23bd
WD
308
309 /*
310 * Install the cpm interrupt handler
311 */
312 irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL);
0de1ffc8
WD
313}
314
7c7a23bd 315/************************************************************************/
0de1ffc8
WD
316
317volatile ulong timestamp = 0;
318
319/*
320 * timer_interrupt - gets called when the decrementer overflows,
321 * with interrupts disabled.
322 * Trivial implementation - no need to be really accurate.
323 */
7c7a23bd 324void timer_interrupt (struct pt_regs *regs)
0de1ffc8 325{
7c7a23bd
WD
326 volatile immap_t *immr = (immap_t *) CFG_IMMR;
327
0de1ffc8 328#ifdef CONFIG_STATUS_LED
7c7a23bd 329 extern void status_led_tick (ulong);
0de1ffc8
WD
330#endif
331#if 0
332 printf ("*** Timer Interrupt *** ");
333#endif
334 /* Reset Timer Expired and Timers Interrupt Status */
335 immr->im_clkrstk.cark_plprcrk = KAPWR_KEY;
7c7a23bd 336 __asm__ ("nop");
2535d602
WD
337#ifdef CONFIG_MPC866_et_al
338 immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
339#else
0de1ffc8 340 immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS | PLPRCR_TMIST;
2535d602 341#endif
0de1ffc8
WD
342 /* Restore Decrementer Count */
343 set_dec (decrementer_count);
344
345 timestamp++;
346
347#ifdef CONFIG_STATUS_LED
348 status_led_tick (timestamp);
7c7a23bd 349#endif /* CONFIG_STATUS_LED */
0de1ffc8
WD
350
351#if defined(CONFIG_WATCHDOG) || defined(CFG_CMA_LCD_HEARTBEAT)
352
0de1ffc8
WD
353 /*
354 * The shortest watchdog period of all boards (except LWMON)
355 * is approx. 1 sec, thus re-trigger watchdog at least
356 * every 500 ms = CFG_HZ / 2
357 */
358#ifndef CONFIG_LWMON
359 if ((timestamp % (CFG_HZ / 2)) == 0) {
360#else
361 if ((timestamp % (CFG_HZ / 20)) == 0) {
362#endif
363
364#if defined(CFG_CMA_LCD_HEARTBEAT)
7c7a23bd
WD
365 extern void lcd_heartbeat (void);
366
367 lcd_heartbeat ();
0de1ffc8
WD
368#endif /* CFG_CMA_LCD_HEARTBEAT */
369
370#if defined(CONFIG_WATCHDOG)
7c7a23bd 371 reset_8xx_watchdog (immr);
0de1ffc8
WD
372#endif /* CONFIG_WATCHDOG */
373
374 }
0de1ffc8
WD
375#endif /* CONFIG_WATCHDOG || CFG_CMA_LCD_HEARTBEAT */
376}
377
7c7a23bd 378/************************************************************************/
0de1ffc8
WD
379
380void reset_timer (void)
381{
382 timestamp = 0;
383}
384
385ulong get_timer (ulong base)
386{
387 return (timestamp - base);
388}
389
390void set_timer (ulong t)
391{
392 timestamp = t;
393}
394
7c7a23bd 395/************************************************************************/
This page took 0.078023 seconds and 4 git commands to generate.