interrupt.c revision 84541
1/* $FreeBSD: head/sys/ia64/ia64/interrupt.c 84541 2001-10-05 10:30:09Z dfr $ */
2/* $NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $ */
3
4/*
5 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
6 * All rights reserved.
7 *
8 * Authors: Keith Bostic, Chris G. Demetriou
9 *
10 * Permission to use, copy, modify and distribute this software and
11 * its documentation is hereby granted, provided that both the copyright
12 * notice and this permission notice appear in all copies of the
13 * software, derivative works or modified versions, and any portions
14 * thereof, and that both notices appear in supporting documentation.
15 *
16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
17 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19 *
20 * Carnegie Mellon requests users of this software to return to
21 *
22 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
23 *  School of Computer Science
24 *  Carnegie Mellon University
25 *  Pittsburgh PA 15213-3890
26 *
27 * any improvements or extensions that they make and grant Carnegie the
28 * rights to redistribute these changes.
29 */
30/*
31 * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
32 * Redistribute and modify at will, leaving only this additional copyright
33 * notice.
34 */
35
36#include "opt_ddb.h"
37
38#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
39
40/* __KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $");*/
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/kernel.h>
45#include <sys/proc.h>
46#include <sys/vmmeter.h>
47#include <sys/bus.h>
48#include <sys/malloc.h>
49#include <sys/ktr.h>
50#include <sys/lock.h>
51#include <sys/mutex.h>
52
53#include <machine/clock.h>
54#include <machine/reg.h>
55#include <machine/frame.h>
56#include <machine/intr.h>
57#include <machine/sapicvar.h>
58
59#ifdef EVCNT_COUNTERS
60struct evcnt clock_intr_evcnt;	/* event counter for clock intrs. */
61#else
62#include <sys/interrupt.h>
63#include <machine/intrcnt.h>
64#endif
65
66#ifdef DDB
67#include <ddb/ddb.h>
68#endif
69
70volatile int mc_expected, mc_received;
71
72static void
73dummy_perf(unsigned long vector, struct trapframe *framep)
74{
75	printf("performance interrupt!\n");
76}
77
78void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf;
79
80
81static u_int schedclk2;
82
83void
84interrupt(u_int64_t vector, struct trapframe *framep)
85{
86	struct thread *td;
87	volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
88
89	td = curthread;
90	atomic_add_int(&td->td_intr_nesting_level, 1);
91
92	/*
93	 * Handle ExtINT interrupts by generating an INTA cycle to
94	 * read the vector.
95	 */
96	if (vector == 0) {
97		vector = ib->ib_inta;
98		printf("ExtINT interrupt: vector=%ld\n", vector);
99		goto out;	/* XXX */
100	}
101
102	if (vector == 240) {/* clock interrupt */
103		/* CTR0(KTR_INTR, "clock interrupt"); */
104
105		cnt.v_intr++;
106#ifdef EVCNT_COUNTERS
107		clock_intr_evcnt.ev_count++;
108#else
109		intrcnt[INTRCNT_CLOCK]++;
110#endif
111		handleclock(framep);
112
113		/* divide hz (1024) by 8 to get stathz (128) */
114		if((++schedclk2 & 0x7) == 0)
115			statclock((struct clockframe *)framep);
116	} else {
117		ia64_dispatch_intr(framep, vector);
118	}
119
120 out:
121	atomic_subtract_int(&td->td_intr_nesting_level, 1);
122}
123
124int
125badaddr(addr, size)
126	void *addr;
127	size_t size;
128{
129	return(badaddr_read(addr, size, NULL));
130}
131
132int
133badaddr_read(addr, size, rptr)
134	void *addr;
135	size_t size;
136	void *rptr;
137{
138	return (1);		/* XXX implement */
139}
140
141/*
142 * Hardware irqs have vectors starting at this offset.
143 */
144#define IA64_HARDWARE_IRQ_BASE	0x20
145
146struct ia64_intr {
147    struct ithd		*ithd;  /* interrupt thread */
148    volatile long	*cntp;  /* interrupt counter */
149};
150
151static struct sapic *ia64_sapics[16]; /* XXX make this resizable */
152static int ia64_sapic_count;
153static struct mtx ia64_intrs_lock;
154static struct ia64_intr *ia64_intrs[256];
155
156static void	ithds_init(void *dummy);
157
158static void
159ithds_init(void *dummy)
160{
161
162	mtx_init(&ia64_intrs_lock, "ithread table lock", MTX_SPIN);
163}
164SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL);
165
166void
167ia64_add_sapic(struct sapic *sa)
168{
169	ia64_sapics[ia64_sapic_count++] = sa;
170}
171
172static void
173ia64_enable(int vector)
174{
175	int irq, i;
176
177	irq = vector - IA64_HARDWARE_IRQ_BASE;
178	for (i = 0; i < ia64_sapic_count; i++) {
179		struct sapic *sa = ia64_sapics[i];
180		if (irq >= sa->sa_base && irq <= sa->sa_limit)
181			sapic_enable(sa, irq - sa->sa_base, vector,
182				     (irq < 16
183				      ? SAPIC_TRIGGER_EDGE
184				      : SAPIC_TRIGGER_LEVEL),
185				     (irq < 16
186				      ? SAPIC_POLARITY_HIGH
187				      : SAPIC_POLARITY_LOW));
188	}
189}
190
191static void
192ia64_send_eoi(int vector)
193{
194	int irq, i;
195
196	irq = vector - IA64_HARDWARE_IRQ_BASE;
197	for (i = 0; i < ia64_sapic_count; i++) {
198		struct sapic *sa = ia64_sapics[i];
199		if (irq >= sa->sa_base && irq <= sa->sa_limit)
200			sapic_eoi(sa, vector);
201	}
202}
203
204int
205ia64_setup_intr(const char *name, int irq, driver_intr_t handler, void *arg,
206		enum intr_type flags, void **cookiep, volatile long *cntp)
207{
208	struct ia64_intr *i;
209	int errcode;
210	int vector = irq + IA64_HARDWARE_IRQ_BASE;
211
212	/*
213	 * XXX - Can we have more than one device on a vector?  If so, we have
214	 * a race condition here that needs to be worked around similar to
215	 * the fashion done in the i386 inthand_add() function.
216	 */
217
218	/* First, check for an existing hash table entry for this vector. */
219	mtx_lock_spin(&ia64_intrs_lock);
220	i = ia64_intrs[vector];
221	mtx_unlock_spin(&ia64_intrs_lock);
222
223	if (i == NULL) {
224		/* None was found, so create an entry. */
225		i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
226		if (i == NULL)
227			return ENOMEM;
228		i->cntp = cntp;
229		errcode = ithread_create(&i->ithd, vector, 0, 0,
230					 ia64_send_eoi, "intr:");
231		if (errcode) {
232			free(i, M_DEVBUF);
233			return errcode;
234		}
235
236		mtx_lock_spin(&ia64_intrs_lock);
237		ia64_intrs[vector] = i;
238		mtx_unlock_spin(&ia64_intrs_lock);
239	}
240
241	/* Second, add this handler. */
242	errcode = ithread_add_handler(i->ithd, name, handler, arg,
243	    ithread_priority(flags), flags, cookiep);
244	if (errcode)
245		return errcode;
246
247	ia64_enable(vector);
248	return 0;
249}
250
251int
252ia64_teardown_intr(void *cookie)
253{
254
255	return (ithread_remove_handler(cookie));
256}
257
258void
259ia64_dispatch_intr(void *frame, unsigned long vector)
260{
261	struct ia64_intr *i;
262	struct ithd *ithd;			/* our interrupt thread */
263	struct intrhand *ih;
264	int error;
265
266	/*
267	 * Find the interrupt thread for this vector.
268	 */
269	i = ia64_intrs[vector];
270	if (i == NULL)
271		return;			/* no ithread for this vector */
272
273	ithd = i->ithd;
274	KASSERT(ithd != NULL, ("interrupt vector without a thread"));
275
276	/*
277	 * As an optomization, if an ithread has no handlers, don't
278	 * schedule it to run.
279	 */
280	if (TAILQ_EMPTY(&ithd->it_handlers))
281		return;
282
283	if (i->cntp)
284		atomic_add_long(i->cntp, 1);
285
286	/*
287	 * Handle a fast interrupt if there is no actual thread for this
288	 * interrupt by calling the handler directly without Giant.  Note
289	 * that this means that any fast interrupt handler must be MP safe.
290	 */
291	ih = TAILQ_FIRST(&ithd->it_handlers);
292	if ((ih->ih_flags & IH_FAST) != 0) {
293		ih->ih_handler(ih->ih_argument);
294		ia64_send_eoi(vector);
295		return;
296	}
297
298	error = ithread_schedule(ithd, 0);	/* XXX:no preemption for now */
299	KASSERT(error == 0, ("got an impossible stray interrupt"));
300}
301
302