blob: 8bc0a1afdea3dd0b9c9601ac3e92b66c49f27bbf [file] [log] [blame]
wdenk0de1ffc2002-10-25 20:52:57 +00001/*
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
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
wdenk7c7a23b2002-12-07 00:20:59 +000031/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +000032
wdenk7c7a23b2002-12-07 00:20:59 +000033unsigned decrementer_count; /* count value for 1e6/HZ microseconds */
wdenk0de1ffc2002-10-25 20:52:57 +000034
wdenk7c7a23b2002-12-07 00:20:59 +000035/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +000036
37/*
38 * CPM interrupt vector functions.
39 */
wdenk7c7a23b2002-12-07 00:20:59 +000040struct interrupt_action {
41 interrupt_handler_t *handler;
42 void *arg;
wdenk0de1ffc2002-10-25 20:52:57 +000043};
44
wdenk7c7a23b2002-12-07 00:20:59 +000045static struct interrupt_action cpm_vecs[CPMVEC_NR];
46static struct interrupt_action irq_vecs[NR_IRQS];
wdenk0de1ffc2002-10-25 20:52:57 +000047
48static void cpm_interrupt_init (void);
wdenk7c7a23b2002-12-07 00:20:59 +000049static void cpm_interrupt (void *regs);
wdenk0de1ffc2002-10-25 20:52:57 +000050
wdenk7c7a23b2002-12-07 00:20:59 +000051/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +000052
wdenk7c7a23b2002-12-07 00:20:59 +000053static __inline__ unsigned long get_msr (void)
wdenk0de1ffc2002-10-25 20:52:57 +000054{
wdenk7c7a23b2002-12-07 00:20:59 +000055 unsigned long msr;
wdenk0de1ffc2002-10-25 20:52:57 +000056
wdenk7c7a23b2002-12-07 00:20:59 +000057 asm volatile ("mfmsr %0":"=r" (msr):);
58
59 return msr;
wdenk0de1ffc2002-10-25 20:52:57 +000060}
61
wdenk7c7a23b2002-12-07 00:20:59 +000062static __inline__ void set_msr (unsigned long msr)
wdenk0de1ffc2002-10-25 20:52:57 +000063{
wdenk7c7a23b2002-12-07 00:20:59 +000064 asm volatile ("mtmsr %0"::"r" (msr));
wdenk0de1ffc2002-10-25 20:52:57 +000065}
66
wdenk7c7a23b2002-12-07 00:20:59 +000067static __inline__ unsigned long get_dec (void)
wdenk0de1ffc2002-10-25 20:52:57 +000068{
wdenk7c7a23b2002-12-07 00:20:59 +000069 unsigned long val;
wdenk0de1ffc2002-10-25 20:52:57 +000070
wdenk7c7a23b2002-12-07 00:20:59 +000071 asm volatile ("mfdec %0":"=r" (val):);
72
73 return val;
wdenk0de1ffc2002-10-25 20:52:57 +000074}
75
76
wdenk7c7a23b2002-12-07 00:20:59 +000077static __inline__ void set_dec (unsigned long val)
wdenk0de1ffc2002-10-25 20:52:57 +000078{
wdenk7c7a23b2002-12-07 00:20:59 +000079 asm volatile ("mtdec %0"::"r" (val));
wdenk0de1ffc2002-10-25 20:52:57 +000080}
81
82
83void enable_interrupts (void)
84{
wdenk7c7a23b2002-12-07 00:20:59 +000085 set_msr (get_msr () | MSR_EE);
wdenk0de1ffc2002-10-25 20:52:57 +000086}
87
88/* returns flag if MSR_EE was set before */
89int disable_interrupts (void)
90{
wdenk7c7a23b2002-12-07 00:20:59 +000091 ulong msr = get_msr ();
92
wdenk0de1ffc2002-10-25 20:52:57 +000093 set_msr (msr & ~MSR_EE);
94 return ((msr & MSR_EE) != 0);
95}
96
wdenk7c7a23b2002-12-07 00:20:59 +000097/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +000098
wdenk7c7a23b2002-12-07 00:20:59 +000099int interrupt_init (void)
wdenk0de1ffc2002-10-25 20:52:57 +0000100{
wdenk7c7a23b2002-12-07 00:20:59 +0000101 volatile immap_t *immr = (immap_t *) CFG_IMMR;
wdenk0de1ffc2002-10-25 20:52:57 +0000102
wdenk7c7a23b2002-12-07 00:20:59 +0000103 decrementer_count = get_tbclk () / CFG_HZ;
wdenk0de1ffc2002-10-25 20:52:57 +0000104
wdenk7c7a23b2002-12-07 00:20:59 +0000105 /* disable all interrupts */
106 immr->im_siu_conf.sc_simask = 0;
wdenk0de1ffc2002-10-25 20:52:57 +0000107
wdenk7c7a23b2002-12-07 00:20:59 +0000108 /* Configure CPM interrupts */
109 cpm_interrupt_init ();
wdenk0de1ffc2002-10-25 20:52:57 +0000110
111 set_dec (decrementer_count);
112
wdenk7c7a23b2002-12-07 00:20:59 +0000113 set_msr (get_msr () | MSR_EE);
wdenk0de1ffc2002-10-25 20:52:57 +0000114
115 return (0);
116}
117
wdenk7c7a23b2002-12-07 00:20:59 +0000118/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000119
120/*
121 * Handle external interrupts
122 */
wdenk7c7a23b2002-12-07 00:20:59 +0000123void external_interrupt (struct pt_regs *regs)
wdenk0de1ffc2002-10-25 20:52:57 +0000124{
wdenk7c7a23b2002-12-07 00:20:59 +0000125 volatile immap_t *immr = (immap_t *) CFG_IMMR;
126 int irq;
127 ulong simask, newmask;
128 ulong vec, v_bit;
wdenk0de1ffc2002-10-25 20:52:57 +0000129
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
wdenk7c7a23b2002-12-07 00:20:59 +0000145 if (!(irq & 0x1)) { /* External Interrupt ? */
wdenk0de1ffc2002-10-25 20:52:57 +0000146 ulong siel;
wdenk7c7a23b2002-12-07 00:20:59 +0000147
wdenk0de1ffc2002-10-25 20:52:57 +0000148 /*
149 * Read Interrupt Edge/Level Register
150 */
151 siel = immr->im_siu_conf.sc_siel;
152
wdenk7c7a23b2002-12-07 00:20:59 +0000153 if (siel & v_bit) { /* edge triggered interrupt ? */
wdenk0de1ffc2002-10-25 20:52:57 +0000154 /*
155 * Rewrite SIPEND Register to clear interrupt
156 */
157 immr->im_siu_conf.sc_sipend = v_bit;
158 }
159 }
160
wdenk7c7a23b2002-12-07 00:20:59 +0000161 if (irq_vecs[irq].handler != NULL) {
162 irq_vecs[irq].handler (irq_vecs[irq].arg);
163 } else {
wdenk0de1ffc2002-10-25 20:52:57 +0000164 printf ("\nBogus External Interrupt IRQ %d Vector %ld\n",
wdenk7c7a23b2002-12-07 00:20:59 +0000165 irq, vec);
wdenk0de1ffc2002-10-25 20:52:57 +0000166 /* turn off the bogus interrupt to avoid it from now */
167 simask &= ~v_bit;
wdenk0de1ffc2002-10-25 20:52:57 +0000168 }
wdenk0de1ffc2002-10-25 20:52:57 +0000169 /*
170 * Re-Enable old Interrupt Mask
171 */
172 immr->im_siu_conf.sc_simask = simask;
173}
174
wdenk7c7a23b2002-12-07 00:20:59 +0000175/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000176
177/*
178 * CPM interrupt handler
179 */
wdenk7c7a23b2002-12-07 00:20:59 +0000180static void cpm_interrupt (void *regs)
wdenk0de1ffc2002-10-25 20:52:57 +0000181{
wdenk7c7a23b2002-12-07 00:20:59 +0000182 volatile immap_t *immr = (immap_t *) CFG_IMMR;
183 uint vec;
wdenk0de1ffc2002-10-25 20:52:57 +0000184
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) {
wdenk7c7a23b2002-12-07 00:20:59 +0000194 (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
wdenk0de1ffc2002-10-25 20:52:57 +0000195 } else {
196 immr->im_cpic.cpic_cimr &= ~(1 << vec);
197 printf ("Masking bogus CPM interrupt vector 0x%x\n", vec);
198 }
199 /*
wdenk7c7a23b2002-12-07 00:20:59 +0000200 * After servicing the interrupt,
201 * we have to remove the status indicator.
wdenk0de1ffc2002-10-25 20:52:57 +0000202 */
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 */
wdenk7c7a23b2002-12-07 00:20:59 +0000212static void cpm_error_interrupt (void *dummy)
wdenk0de1ffc2002-10-25 20:52:57 +0000213{
214}
215
wdenk7c7a23b2002-12-07 00:20:59 +0000216/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000217/*
wdenk7c7a23b2002-12-07 00:20:59 +0000218 * Install and free an interrupt handler
wdenk0de1ffc2002-10-25 20:52:57 +0000219 */
wdenk7c7a23b2002-12-07 00:20:59 +0000220void irq_install_handler (int vec, interrupt_handler_t * handler,
221 void *arg)
wdenk0de1ffc2002-10-25 20:52:57 +0000222{
wdenk7c7a23b2002-12-07 00:20:59 +0000223 volatile immap_t *immr = (immap_t *) CFG_IMMR;
wdenk0de1ffc2002-10-25 20:52:57 +0000224
wdenk7c7a23b2002-12-07 00:20:59 +0000225 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);
251#if 0
252 printf ("Install SIU interrupt for vector %d ==> %p\n",
253 vec, handler);
254#endif
wdenk0de1ffc2002-10-25 20:52:57 +0000255 }
wdenk0de1ffc2002-10-25 20:52:57 +0000256}
257
wdenk7c7a23b2002-12-07 00:20:59 +0000258void irq_free_handler (int vec)
wdenk0de1ffc2002-10-25 20:52:57 +0000259{
wdenk7c7a23b2002-12-07 00:20:59 +0000260 volatile immap_t *immr = (immap_t *) CFG_IMMR;
261
262 if ((vec & CPMVEC_OFFSET) != 0) {
263 /* CPM interrupt */
264 vec &= 0xffff;
wdenk0de1ffc2002-10-25 20:52:57 +0000265#if 0
wdenk7c7a23b2002-12-07 00:20:59 +0000266 printf ("Free CPM interrupt for vector %d ==> %p\n",
267 vec, cpm_vecs[vec].handler);
wdenk0de1ffc2002-10-25 20:52:57 +0000268#endif
wdenk7c7a23b2002-12-07 00:20:59 +0000269 immr->im_cpic.cpic_cimr &= ~(1 << vec);
270 cpm_vecs[vec].handler = NULL;
271 cpm_vecs[vec].arg = NULL;
272 } else {
273 /* SIU interrupt */
274#if 0
275 printf ("Free CPM interrupt for vector %d ==> %p\n",
276 vec, cpm_vecs[vec].handler);
277#endif
278 immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec));
279 irq_vecs[vec].handler = NULL;
280 irq_vecs[vec].arg = NULL;
281 }
wdenk0de1ffc2002-10-25 20:52:57 +0000282}
283
wdenk7c7a23b2002-12-07 00:20:59 +0000284/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000285
wdenk7c7a23b2002-12-07 00:20:59 +0000286static void cpm_interrupt_init (void)
wdenk0de1ffc2002-10-25 20:52:57 +0000287{
wdenk7c7a23b2002-12-07 00:20:59 +0000288 volatile immap_t *immr = (immap_t *) CFG_IMMR;
wdenk0de1ffc2002-10-25 20:52:57 +0000289
290 /*
291 * Initialize the CPM interrupt controller.
292 */
293
294 immr->im_cpic.cpic_cicr =
wdenk7c7a23b2002-12-07 00:20:59 +0000295 (CICR_SCD_SCC4 |
296 CICR_SCC_SCC3 |
297 CICR_SCB_SCC2 |
298 CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
wdenk0de1ffc2002-10-25 20:52:57 +0000299
300 immr->im_cpic.cpic_cimr = 0;
301
302 /*
303 * Install the error handler.
304 */
wdenk7c7a23b2002-12-07 00:20:59 +0000305 irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL);
wdenk0de1ffc2002-10-25 20:52:57 +0000306
307 immr->im_cpic.cpic_cicr |= CICR_IEN;
wdenk7c7a23b2002-12-07 00:20:59 +0000308
309 /*
310 * Install the cpm interrupt handler
311 */
312 irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL);
wdenk0de1ffc2002-10-25 20:52:57 +0000313}
314
wdenk7c7a23b2002-12-07 00:20:59 +0000315/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000316
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 */
wdenk7c7a23b2002-12-07 00:20:59 +0000324void timer_interrupt (struct pt_regs *regs)
wdenk0de1ffc2002-10-25 20:52:57 +0000325{
wdenk7c7a23b2002-12-07 00:20:59 +0000326 volatile immap_t *immr = (immap_t *) CFG_IMMR;
327
wdenk0de1ffc2002-10-25 20:52:57 +0000328#ifdef CONFIG_STATUS_LED
wdenk7c7a23b2002-12-07 00:20:59 +0000329 extern void status_led_tick (ulong);
wdenk0de1ffc2002-10-25 20:52:57 +0000330#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;
wdenk7c7a23b2002-12-07 00:20:59 +0000336 __asm__ ("nop");
wdenk2535d602003-07-17 23:16:40 +0000337#ifdef CONFIG_MPC866_et_al
338 immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
339#else
wdenk0de1ffc2002-10-25 20:52:57 +0000340 immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS | PLPRCR_TMIST;
wdenk2535d602003-07-17 23:16:40 +0000341#endif
wdenk0de1ffc2002-10-25 20:52:57 +0000342 /* Restore Decrementer Count */
343 set_dec (decrementer_count);
344
345 timestamp++;
346
347#ifdef CONFIG_STATUS_LED
348 status_led_tick (timestamp);
wdenk7c7a23b2002-12-07 00:20:59 +0000349#endif /* CONFIG_STATUS_LED */
wdenk0de1ffc2002-10-25 20:52:57 +0000350
351#if defined(CONFIG_WATCHDOG) || defined(CFG_CMA_LCD_HEARTBEAT)
352
wdenk0de1ffc2002-10-25 20:52:57 +0000353 /*
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)
wdenk7c7a23b2002-12-07 00:20:59 +0000365 extern void lcd_heartbeat (void);
366
367 lcd_heartbeat ();
wdenk0de1ffc2002-10-25 20:52:57 +0000368#endif /* CFG_CMA_LCD_HEARTBEAT */
369
370#if defined(CONFIG_WATCHDOG)
wdenk7c7a23b2002-12-07 00:20:59 +0000371 reset_8xx_watchdog (immr);
wdenk0de1ffc2002-10-25 20:52:57 +0000372#endif /* CONFIG_WATCHDOG */
373
374 }
wdenk0de1ffc2002-10-25 20:52:57 +0000375#endif /* CONFIG_WATCHDOG || CFG_CMA_LCD_HEARTBEAT */
376}
377
wdenk7c7a23b2002-12-07 00:20:59 +0000378/************************************************************************/
wdenk0de1ffc2002-10-25 20:52:57 +0000379
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
wdenk7c7a23b2002-12-07 00:20:59 +0000395/************************************************************************/