xref: /openbmc/u-boot/arch/nios2/cpu/interrupts.c (revision c4f07be2)
1 /*
2  * (C) Copyright 2000-2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2004, 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 <nios2.h>
29 #include <nios2-io.h>
30 #include <asm/types.h>
31 #include <asm/io.h>
32 #include <asm/ptrace.h>
33 #include <common.h>
34 #include <command.h>
35 #include <watchdog.h>
36 #ifdef CONFIG_STATUS_LED
37 #include <status_led.h>
38 #endif
39 
40 #if defined(CONFIG_SYS_NIOS_TMRBASE) && !defined(CONFIG_SYS_NIOS_TMRIRQ)
41 #error CONFIG_SYS_NIOS_TMRIRQ not defined (see documentation)
42 #endif
43 
44 /****************************************************************************/
45 
46 struct	irq_action {
47 	interrupt_handler_t *handler;
48 	void *arg;
49 	int count;
50 };
51 
52 static struct irq_action vecs[32];
53 
54 /*************************************************************************/
55 volatile ulong timestamp = 0;
56 
57 void reset_timer (void)
58 {
59 	nios_timer_t *tmr =(nios_timer_t *)CONFIG_SYS_NIOS_TMRBASE;
60 
61 	/* From Embedded Peripherals Handbook:
62 	 *
63 	 * "When the hardware is configured with Writeable period
64 	 * disabled, writing to one of the period_n registers causes
65 	 * the counter to reset to the fixed Timeout Period specified
66 	 * at system generation time."
67 	 *
68 	 * Here we force a reload to prevent early timeouts from
69 	 * get_timer() when the interrupt period is greater than
70 	 * than 1 msec.
71 	 *
72 	 * Simply write to periodl with its own value to force an
73 	 * internal counter reload, THEN reset the timestamp.
74 	 */
75 	writel (readl (&tmr->periodl), &tmr->periodl);
76 	timestamp = 0;
77 
78 	/* From Embedded Peripherals Handbook:
79 	 *
80 	 * "Writing to one of the period_n registers stops the internal
81 	 * counter, except when the hardware is configured with Start/Stop
82 	 * control bits off. If Start/Stop control bits is off, writing
83 	 * either register does not stop the counter."
84 	 *
85 	 * In order to accomodate either configuration, the control
86 	 * register is re-written. If the counter is stopped, it will
87 	 * be restarted. If it is running, the write is essentially
88 	 * a nop.
89 	 */
90 	writel (NIOS_TIMER_ITO | NIOS_TIMER_CONT | NIOS_TIMER_START,
91 			&tmr->control);
92 
93 }
94 
95 ulong get_timer (ulong base)
96 {
97 	WATCHDOG_RESET ();
98 	return (timestamp - base);
99 }
100 
101 /* The board must handle this interrupt if a timer is not
102  * provided.
103  */
104 #if defined(CONFIG_SYS_NIOS_TMRBASE)
105 void tmr_isr (void *arg)
106 {
107 	nios_timer_t *tmr = (nios_timer_t *)arg;
108 	/* Interrupt is cleared by writing anything to the
109 	 * status register.
110 	 */
111 	writel (0, &tmr->status);
112 	timestamp += CONFIG_SYS_NIOS_TMRMS;
113 #ifdef CONFIG_STATUS_LED
114 	status_led_tick(timestamp);
115 #endif
116 }
117 
118 static void tmr_init (void)
119 {
120 	nios_timer_t *tmr =(nios_timer_t *)CONFIG_SYS_NIOS_TMRBASE;
121 
122 	writel (0, &tmr->status);
123 	writel (0, &tmr->control);
124 	writel (NIOS_TIMER_STOP, &tmr->control);
125 
126 #if defined(CONFIG_SYS_NIOS_TMRCNT)
127 	writel (CONFIG_SYS_NIOS_TMRCNT & 0xffff, &tmr->periodl);
128 	writel ((CONFIG_SYS_NIOS_TMRCNT >> 16) & 0xffff, &tmr->periodh);
129 #endif
130 	writel (NIOS_TIMER_ITO | NIOS_TIMER_CONT | NIOS_TIMER_START,
131 			&tmr->control);
132 	irq_install_handler (CONFIG_SYS_NIOS_TMRIRQ, tmr_isr, (void *)tmr);
133 }
134 
135 #endif /* CONFIG_SYS_NIOS_TMRBASE */
136 
137 /*************************************************************************/
138 int disable_interrupts (void)
139 {
140 	int val = rdctl (CTL_STATUS);
141 	wrctl (CTL_STATUS, val & ~STATUS_IE);
142 	return (val & STATUS_IE);
143 }
144 
145 void enable_interrupts( void )
146 {
147 	int val = rdctl (CTL_STATUS);
148 	wrctl (CTL_STATUS, val | STATUS_IE);
149 }
150 
151 void external_interrupt (struct pt_regs *regs)
152 {
153 	unsigned irqs;
154 	struct irq_action *act;
155 
156 	/* Evaluate only irqs that are both enabled AND pending */
157 	irqs = rdctl (CTL_IENABLE) & rdctl (CTL_IPENDING);
158 	act = vecs;
159 
160 	/* Assume (as does the Nios2 HAL) that bit 0 is highest
161 	 * priority. NOTE: There is ALWAYS a handler assigned
162 	 * (the default if no other).
163 	 */
164 	while (irqs) {
165 		if (irqs & 1) {
166 			act->handler (act->arg);
167 			act->count++;
168 		}
169 		irqs >>=1;
170 		act++;
171 	}
172 }
173 
174 static void def_hdlr (void *arg)
175 {
176 	unsigned irqs = rdctl (CTL_IENABLE);
177 
178 	/* Disable the individual interrupt -- with gratuitous
179 	 * warning.
180 	 */
181 	irqs &= ~(1 << (int)arg);
182 	wrctl (CTL_IENABLE, irqs);
183 	printf ("WARNING: Disabling unhandled interrupt: %d\n",
184 			(int)arg);
185 }
186 
187 /*************************************************************************/
188 void irq_install_handler (int irq, interrupt_handler_t *hdlr, void *arg)
189 {
190 
191 	int flag;
192 	struct irq_action *act;
193 	unsigned ena = rdctl (CTL_IENABLE);
194 
195 	if ((irq < 0) || (irq > 31))
196 		return;
197 	act = &vecs[irq];
198 
199 	flag = disable_interrupts ();
200 	if (hdlr) {
201 		act->handler = hdlr;
202 		act->arg = arg;
203 		ena |= (1 << irq);		/* enable */
204 	} else {
205 		act->handler = def_hdlr;
206 		act->arg = (void *)irq;
207 		ena &= ~(1 << irq);		/* disable */
208 	}
209 	wrctl (CTL_IENABLE, ena);
210 	if (flag) enable_interrupts ();
211 }
212 
213 
214 int interrupt_init (void)
215 {
216 	int i;
217 
218 	/* Assign the default handler to all */
219 	for (i = 0; i < 32; i++) {
220 		vecs[i].handler = def_hdlr;
221 		vecs[i].arg = (void *)i;
222 		vecs[i].count = 0;
223 	}
224 
225 #if defined(CONFIG_SYS_NIOS_TMRBASE)
226 	tmr_init ();
227 #endif
228 
229 	enable_interrupts ();
230 	return (0);
231 }
232 
233 
234 /*************************************************************************/
235 #if defined(CONFIG_CMD_IRQ)
236 int do_irqinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
237 {
238 	int i;
239 	struct irq_action *act = vecs;
240 
241 	printf ("\nInterrupt-Information:\n\n");
242 	printf ("Nr  Routine   Arg       Count\n");
243 	printf ("-----------------------------\n");
244 
245 	for (i=0; i<32; i++) {
246 		if (act->handler != def_hdlr) {
247 			printf ("%02d  %08lx  %08lx  %d\n",
248 				i,
249 				(ulong)act->handler,
250 				(ulong)act->arg,
251 				act->count);
252 		}
253 		act++;
254 	}
255 	printf ("\n");
256 
257 	return (0);
258 }
259 #endif
260