Patches by Stephan Linz, 11 Dec 2003:
[platform/kernel/u-boot.git] / cpu / nios / interrupts.c
1 /*
2  * (C) Copyright 2000-2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2003, Psyent Corporation <www.psyent.com>
6  * Scott McNutt <smcnutt@psyent.com>
7  *
8  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26
27
28 #include <nios.h>
29 #include <nios-io.h>
30 #include <asm/ptrace.h>
31 #include <common.h>
32 #include <command.h>
33 #ifdef CONFIG_STATUS_LED
34 #include <status_led.h>
35 #endif
36
37 /****************************************************************************/
38
39 struct  irq_action {
40         interrupt_handler_t *handler;
41         void *arg;
42         int count;
43 };
44
45 static struct irq_action irq_vecs[64];
46
47 /*************************************************************************/
48 volatile ulong timestamp = 0;
49
50 void reset_timer (void)
51 {
52         timestamp = 0;
53 }
54
55 ulong get_timer (ulong base)
56 {
57         return (timestamp - base);
58 }
59
60 void set_timer (ulong t)
61 {
62         timestamp = t;
63 }
64
65
66 /* The board must handle this interrupt if a timer is not
67  * provided.
68  */
69 #if defined(CFG_NIOS_TMRBASE)
70 void timer_interrupt (struct pt_regs *regs)
71 {
72         /* Interrupt is cleared by writing anything to the
73          * status register.
74          */
75         nios_timer_t *tmr = (nios_timer_t *)CFG_NIOS_TMRBASE;
76         tmr->status = 0;
77         timestamp += CFG_NIOS_TMRMS;
78 #ifdef CONFIG_STATUS_LED
79         status_led_tick(timestamp);
80 #endif
81 }
82 #endif
83
84 /*************************************************************************/
85 int disable_interrupts (void)
86 {
87         int val = 0;
88
89         /* Writing anything to CLR_IE disables interrupts */
90         val = rdctl (CTL_STATUS);
91         wrctl (CTL_CLR_IE, 0);
92         return (val & STATUS_IE);
93 }
94
95 void enable_interrupts( void )
96 {
97         /* Writing anything SET_IE enables interrupts */
98         wrctl (CTL_SET_IE, 0);
99 }
100
101 void external_interrupt (struct pt_regs *regs)
102 {
103         unsigned vec;
104
105         vec = (regs->status & STATUS_IPRI) >> 9;        /* ipri */
106
107         irq_vecs[vec].count++;
108         if (irq_vecs[vec].handler != NULL) {
109                 (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
110         } else {
111                 /* A sad side-effect of masking a bogus interrupt is
112                  * that lower priority interrupts will also be disabled.
113                  * This is probably not what we want ... so hang insted.
114                  */
115                 printf ("Unhandled interrupt: 0x%x\n", vec);
116                 disable_interrupts ();
117                 hang ();
118         }
119 }
120
121 /*************************************************************************/
122 int interrupt_init (void)
123 {
124         int vec;
125
126 #if defined(CFG_NIOS_TMRBASE)
127         nios_timer_t *tmr = (nios_timer_t *)CFG_NIOS_TMRBASE;
128
129         tmr->control &= ~NIOS_TIMER_ITO;
130         tmr->control |= NIOS_TIMER_STOP;
131 #endif
132
133         for (vec=0; vec<64; vec++ ) {
134                 irq_vecs[vec].handler = NULL;
135                 irq_vecs[vec].arg = NULL;
136                 irq_vecs[vec].count = 0;
137         }
138
139         /* Need timus interruptus -- start the lopri timer */
140 #if defined(CFG_NIOS_TMRBASE)
141         tmr->control |= ( NIOS_TIMER_ITO |
142                           NIOS_TIMER_CONT |
143                           NIOS_TIMER_START );
144         ipri (CFG_NIOS_TMRIRQ + 1);
145 #endif
146         enable_interrupts ();
147         return (0);
148 }
149
150 void irq_install_handler (int vec, interrupt_handler_t *handler, void *arg)
151 {
152         struct irq_action *irqa = irq_vecs;
153         int   i = vec;
154         int flag;
155
156         if (irqa[i].handler != NULL) {
157                 printf ("Interrupt vector %d: handler 0x%x "
158                         "replacing 0x%x\n",
159                         vec, (uint)handler, (uint)irqa[i].handler);
160         }
161
162         flag = disable_interrupts ();
163         irqa[i].handler = handler;
164         irqa[i].arg = arg;
165         if (flag )
166                 enable_interrupts ();
167 }
168
169 /*************************************************************************/
170 #if (CONFIG_COMMANDS & CFG_CMD_IRQ)
171 int do_irqinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
172 {
173         int vec;
174
175         printf ("\nInterrupt-Information:\n");
176         printf ("Nr  Routine   Arg       CouIt's ok to cnt\n");
177
178         for (vec=0; vec<64; vec++) {
179                 if (irq_vecs[vec].handler != NULL) {
180                         printf ("%02d  %08lx  %08lx  %d\n",
181                                 vec,
182                                 (ulong)irq_vecs[vec].handler<<1,
183                                 (ulong)irq_vecs[vec].arg,
184                                 irq_vecs[vec].count);
185                 }
186         }
187
188         return (0);
189 }
190 #endif  /* CONFIG_COMMANDS & CFG_CMD_IRQ */