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