blob: de46d6e37e99fe968f47dfaeb902d2fa4880ed7f [file] [log] [blame]
wdenkc7de8292002-11-19 11:04:11 +00001/*
2 * (C) Copyright 2002
3 * John W. Linville <linville@tuxdriver.com>
4 *
5 * Copied and modified from original code by Josh Huber. Original
6 * copyright notice preserved below.
7 *
8 * (C) Copyright 2001
9 * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc.
10 *
11 * See file CREDITS for list of people who contributed to this
12 * project.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of
17 * the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27 * MA 02111-1307 USA
28 */
29
30/*
31 * interrupts.c - just enough support for the decrementer/timer
32 */
33
34#include <common.h>
35#include <asm/processor.h>
36#include <command.h>
37#include "i8259.h"
38
39#undef DEBUG
40#ifdef DEBUG
41#define PRINTF(fmt,args...) printf (fmt ,##args)
42#else
43#define PRINTF(fmt,args...)
44#endif
45#define NR_IRQS 16
46
47void irq_alloc_init(void);
48long irq_alloc(long wanted);
49
50/****************************************************************************/
51
52unsigned decrementer_count; /* count value for 1e6/HZ microseconds */
53
54struct irq_action {
55 interrupt_handler_t *handler;
56 void *arg;
57 ulong count;
58};
59
60static struct irq_action irq_handlers[NR_IRQS];
61
62/****************************************************************************/
63
64static __inline__ unsigned long
65get_msr(void)
66{
67 unsigned long msr;
68
69 asm volatile("mfmsr %0" : "=r" (msr) :);
70 return msr;
71}
72
73static __inline__ void
74set_msr(unsigned long msr)
75{
wdenk8bde7f72003-06-27 21:31:46 +000076 asm volatile("mtmsr %0" : : "r" (msr));
wdenkc7de8292002-11-19 11:04:11 +000077}
78
79static __inline__ unsigned long
80get_dec(void)
81{
82 unsigned long val;
83
84 asm volatile("mfdec %0" : "=r" (val) :);
85 return val;
86}
87
88
89static __inline__ void
90set_dec(unsigned long val)
91{
wdenk8bde7f72003-06-27 21:31:46 +000092 asm volatile("mtdec %0" : : "r" (val));
wdenkc7de8292002-11-19 11:04:11 +000093}
94
95
96void
97enable_interrupts(void)
98{
99 set_msr (get_msr() | MSR_EE);
100}
101
102/* returns flag if MSR_EE was set before */
103int
104disable_interrupts(void)
105{
106 ulong msr;
107
108 msr = get_msr();
109 set_msr (msr & ~MSR_EE);
110 return ((msr & MSR_EE) != 0);
111}
112
113/****************************************************************************/
114
115int interrupt_init (void)
116{
117 extern void new_reset(void);
118 extern void new_reset_end(void);
119#ifdef DEBUG
120 puts("interrupt_init: setting decrementer_count\n");
121#endif
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200122 decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
wdenkc7de8292002-11-19 11:04:11 +0000123
124#ifdef DEBUG
125 puts("interrupt_init: setting actual decremter\n");
126#endif
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200127 set_dec (get_tbclk() / CONFIG_SYS_HZ);
wdenkc7de8292002-11-19 11:04:11 +0000128
129#ifdef DEBUG
130 puts("interrupt_init: clearing external interrupt table\n");
131#endif
132 /* clear external interrupt table here */
133 memset(irq_handlers, 0, sizeof(irq_handlers));
134
135#ifdef DEBUG
136 puts("interrupt_init: initializing interrupt controller\n");
137#endif
138 i8259_init();
139
140#ifdef DEBUG
141 puts("Copying reset trampoline\n");
142#endif
143 /* WARNING: Assmues that the first megabyte is CACHEINHIBIT! */
144 memcpy((void *)0x100, new_reset, new_reset_end - new_reset);
145
146#ifdef DEBUG
147 PRINTF("interrupt_init: enabling interrupts (msr = %08x)\n",
148 get_msr());
149#endif
150 set_msr (get_msr() | MSR_EE);
151
152#ifdef DEBUG
153 PRINTF("interrupt_init: done. (msr = %08x)\n", get_msr());
154#endif
155
156}
157
158/****************************************************************************/
159
160/*
161 * Handle external interrupts
162 */
163void
164external_interrupt(struct pt_regs *regs)
165{
166 extern int i8259_irq(void);
167
168 int irq, unmask = 1;
169
wdenk8bde7f72003-06-27 21:31:46 +0000170 irq = i8259_irq(); /*i8259_get_irq(regs); */
171/* printf("irq = %d, handler at %p ack=%d\n", irq, irq_handlers[irq].handler, *(volatile unsigned char *)0xFEF00000); */
wdenkc7de8292002-11-19 11:04:11 +0000172 i8259_mask_and_ack(irq);
173
174 if (irq_handlers[irq].handler != NULL)
175 (*irq_handlers[irq].handler)(irq_handlers[irq].arg);
176 else {
177 PRINTF ("\nBogus External Interrupt IRQ %d\n", irq);
178 /*
Wolfgang Denk53677ef2008-05-20 16:00:29 +0200179 * turn off the bogus interrupt, otherwise it
180 * might repeat forever
181 */
wdenkc7de8292002-11-19 11:04:11 +0000182 unmask = 0;
183 }
184
185 if (unmask) i8259_unmask_irq(irq);
186}
187
188volatile ulong timestamp = 0;
189
190/*
191 * timer_interrupt - gets called when the decrementer overflows,
192 * with interrupts disabled.
193 * Trivial implementation - no need to be really accurate.
194 */
195void
196timer_interrupt(struct pt_regs *regs)
197{
198 set_dec(decrementer_count);
199 timestamp++;
200}
201
202/****************************************************************************/
203
204void
205reset_timer(void)
206{
207 timestamp = 0;
208}
209
210ulong
211get_timer(ulong base)
212{
213 return (timestamp - base);
214}
215
216void
217set_timer(ulong t)
218{
219 timestamp = t;
220}
221
222/****************************************************************************/
223
224/*
225 * Install and free a interrupt handler.
226 */
227
228void
229irq_install_handler(int irq, interrupt_handler_t *handler, void *arg)
230{
231 if (irq < 0 || irq >= NR_IRQS) {
232 PRINTF("irq_install_handler: bad irq number %d\n", irq);
233 return;
234 }
235
236 if (irq_handlers[irq].handler != NULL)
237 PRINTF("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
238 (ulong)handler, (ulong)irq_handlers[irq].handler);
239
240 irq_handlers[irq].handler = handler;
241 irq_handlers[irq].arg = arg;
242
243 i8259_unmask_irq(irq);
244}
245
246void
247irq_free_handler(int irq)
248{
249 if (irq < 0 || irq >= NR_IRQS) {
250 PRINTF("irq_free_handler: bad irq number %d\n", irq);
251 return;
252 }
253
254 i8259_mask_irq(irq);
255
256 irq_handlers[irq].handler = NULL;
257 irq_handlers[irq].arg = NULL;
258}
259
260/****************************************************************************/
261
262void
263do_irqinfo(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
264{
265 puts("IRQ related functions are unimplemented currently.\n");
266}