interrupt.c revision 153666
1/* $FreeBSD: head/sys/ia64/ia64/interrupt.c 153666 2005-12-22 22:16:09Z jhb $ */
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#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/proc.h>
44#include <sys/vmmeter.h>
45#include <sys/bus.h>
46#include <sys/malloc.h>
47#include <sys/ktr.h>
48#include <sys/lock.h>
49#include <sys/mutex.h>
50#include <sys/smp.h>
51#include <sys/sysctl.h>
52
53#include <machine/clock.h>
54#include <machine/cpu.h>
55#include <machine/fpu.h>
56#include <machine/frame.h>
57#include <machine/intr.h>
58#include <machine/md_var.h>
59#include <machine/pcb.h>
60#include <machine/reg.h>
61#include <machine/sapicvar.h>
62#include <machine/smp.h>
63
64#ifdef EVCNT_COUNTERS
65struct evcnt clock_intr_evcnt;	/* event counter for clock intrs. */
66#else
67#include <sys/interrupt.h>
68#include <machine/intrcnt.h>
69#endif
70
71#ifdef DDB
72#include <ddb/ddb.h>
73#endif
74
75#ifdef SMP
76extern int mp_ipi_test;
77#endif
78
79volatile int mc_expected, mc_received;
80
81static void
82dummy_perf(unsigned long vector, struct trapframe *tf)
83{
84	printf("performance interrupt!\n");
85}
86
87void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf;
88
89static unsigned int ints[MAXCPU];
90SYSCTL_OPAQUE(_debug, OID_AUTO, ints, CTLFLAG_RW, &ints, sizeof(ints), "IU",
91    "");
92
93static unsigned int clks[MAXCPU];
94#ifdef SMP
95SYSCTL_OPAQUE(_debug, OID_AUTO, clks, CTLFLAG_RW, &clks, sizeof(clks), "IU",
96    "");
97#else
98SYSCTL_INT(_debug, OID_AUTO, clks, CTLFLAG_RW, clks, 0, "");
99#endif
100
101#ifdef SMP
102static unsigned int asts[MAXCPU];
103SYSCTL_OPAQUE(_debug, OID_AUTO, asts, CTLFLAG_RW, &asts, sizeof(asts), "IU",
104    "");
105
106static unsigned int rdvs[MAXCPU];
107SYSCTL_OPAQUE(_debug, OID_AUTO, rdvs, CTLFLAG_RW, &rdvs, sizeof(rdvs), "IU",
108    "");
109#endif
110
111SYSCTL_NODE(_debug, OID_AUTO, clock, CTLFLAG_RW, 0, "clock statistics");
112
113static int adjust_edges = 0;
114SYSCTL_INT(_debug_clock, OID_AUTO, adjust_edges, CTLFLAG_RD,
115    &adjust_edges, 0, "Number of times ITC got more than 12.5% behind");
116
117static int adjust_excess = 0;
118SYSCTL_INT(_debug_clock, OID_AUTO, adjust_excess, CTLFLAG_RD,
119    &adjust_excess, 0, "Total number of ignored ITC interrupts");
120
121static int adjust_lost = 0;
122SYSCTL_INT(_debug_clock, OID_AUTO, adjust_lost, CTLFLAG_RD,
123    &adjust_lost, 0, "Total number of lost ITC interrupts");
124
125static int adjust_ticks = 0;
126SYSCTL_INT(_debug_clock, OID_AUTO, adjust_ticks, CTLFLAG_RD,
127    &adjust_ticks, 0, "Total number of ITC interrupts with adjustment");
128
129int
130interrupt(u_int64_t vector, struct trapframe *tf)
131{
132	struct thread *td;
133	volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
134	uint64_t adj, clk, itc;
135	int64_t delta;
136	int count;
137
138	ia64_set_fpsr(IA64_FPSR_DEFAULT);
139
140	td = curthread;
141	atomic_add_int(&td->td_intr_nesting_level, 1);
142
143	/*
144	 * Handle ExtINT interrupts by generating an INTA cycle to
145	 * read the vector.
146	 */
147	if (vector == 0) {
148		vector = ib->ib_inta;
149		printf("ExtINT interrupt: vector=%ld\n", vector);
150		if (vector == 15)
151			goto stray;
152	}
153
154	if (vector == CLOCK_VECTOR) {/* clock interrupt */
155		/* CTR0(KTR_INTR, "clock interrupt"); */
156
157		PCPU_LAZY_INC(cnt.v_intr);
158#ifdef EVCNT_COUNTERS
159		clock_intr_evcnt.ev_count++;
160#else
161		intrcnt[INTRCNT_CLOCK]++;
162#endif
163		clks[PCPU_GET(cpuid)]++;
164
165		critical_enter();
166
167		adj = PCPU_GET(clockadj);
168		itc = ia64_get_itc();
169		ia64_set_itm(itc + ia64_clock_reload - adj);
170		clk = PCPU_GET(clock);
171		delta = itc - clk;
172		count = 0;
173		while (delta >= ia64_clock_reload) {
174			/* Only the BSP runs the real clock */
175			if (PCPU_GET(cpuid) == 0)
176				hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
177			else
178				hardclock_cpu(TRAPF_USERMODE(tf));
179			if (profprocs != 0)
180				profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
181			statclock(TRAPF_USERMODE(tf));
182			delta -= ia64_clock_reload;
183			clk += ia64_clock_reload;
184			if (adj != 0)
185				adjust_ticks++;
186			count++;
187		}
188		if (count > 0) {
189			adjust_lost += count - 1;
190			if (delta > (ia64_clock_reload >> 3)) {
191				if (adj == 0)
192					adjust_edges++;
193				adj = ia64_clock_reload >> 4;
194			} else
195				adj = 0;
196		} else {
197			adj = 0;
198			adjust_excess++;
199		}
200		PCPU_SET(clock, clk);
201		PCPU_SET(clockadj, adj);
202
203		critical_exit();
204
205#ifdef SMP
206	} else if (vector == ipi_vector[IPI_AST]) {
207		asts[PCPU_GET(cpuid)]++;
208		CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
209	} else if (vector == ipi_vector[IPI_HIGH_FP]) {
210		struct thread *thr = PCPU_GET(fpcurthread);
211		if (thr != NULL) {
212			mtx_lock_spin(&thr->td_md.md_highfp_mtx);
213			save_high_fp(&thr->td_pcb->pcb_high_fp);
214			thr->td_pcb->pcb_fpcpu = NULL;
215			PCPU_SET(fpcurthread, NULL);
216			mtx_unlock_spin(&thr->td_md.md_highfp_mtx);
217		}
218	} else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
219		rdvs[PCPU_GET(cpuid)]++;
220		CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));
221		smp_rendezvous_action();
222	} else if (vector == ipi_vector[IPI_STOP]) {
223		u_int32_t mybit = PCPU_GET(cpumask);
224
225		CTR1(KTR_SMP, "IPI_STOP, cpuid=%d", PCPU_GET(cpuid));
226		savectx(PCPU_GET(pcb));
227		stopped_cpus |= mybit;
228		while ((started_cpus & mybit) == 0)
229			/* spin */;
230		started_cpus &= ~mybit;
231		stopped_cpus &= ~mybit;
232		if (PCPU_GET(cpuid) == 0 && cpustop_restartfunc != NULL) {
233			void (*f)(void) = cpustop_restartfunc;
234			cpustop_restartfunc = NULL;
235			(*f)();
236		}
237	} else if (vector == ipi_vector[IPI_TEST]) {
238		CTR1(KTR_SMP, "IPI_TEST, cpuid=%d", PCPU_GET(cpuid));
239		mp_ipi_test++;
240#endif
241	} else {
242		ints[PCPU_GET(cpuid)]++;
243		ia64_dispatch_intr(tf, vector);
244	}
245
246stray:
247	atomic_subtract_int(&td->td_intr_nesting_level, 1);
248	return (TRAPF_USERMODE(tf));
249}
250
251/*
252 * Hardware irqs have vectors starting at this offset.
253 */
254#define IA64_HARDWARE_IRQ_BASE	0x20
255
256struct ia64_intr {
257    struct intr_event	*event; /* interrupt event */
258    volatile long	*cntp;  /* interrupt counter */
259};
260
261static struct mtx ia64_intrs_lock;
262static struct ia64_intr *ia64_intrs[256];
263
264extern struct sapic *ia64_sapics[];
265extern int ia64_sapic_count;
266
267static void
268ithds_init(void *dummy)
269{
270
271	mtx_init(&ia64_intrs_lock, "intr table", NULL, MTX_SPIN);
272}
273SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL);
274
275static void
276ia64_send_eoi(uintptr_t vector)
277{
278	int irq, i;
279
280	irq = vector - IA64_HARDWARE_IRQ_BASE;
281	for (i = 0; i < ia64_sapic_count; i++) {
282		struct sapic *sa = ia64_sapics[i];
283		if (irq >= sa->sa_base && irq <= sa->sa_limit)
284			sapic_eoi(sa, vector);
285	}
286}
287
288int
289ia64_setup_intr(const char *name, int irq, driver_intr_t handler, void *arg,
290		enum intr_type flags, void **cookiep, volatile long *cntp)
291{
292	struct ia64_intr *i;
293	int errcode;
294	intptr_t vector = irq + IA64_HARDWARE_IRQ_BASE;
295	char *intrname;
296
297	/*
298	 * XXX - Can we have more than one device on a vector?  If so, we have
299	 * a race condition here that needs to be worked around similar to
300	 * the fashion done in the i386 inthand_add() function.
301	 */
302
303	/* First, check for an existing hash table entry for this vector. */
304	mtx_lock_spin(&ia64_intrs_lock);
305	i = ia64_intrs[vector];
306	mtx_unlock_spin(&ia64_intrs_lock);
307
308	if (i == NULL) {
309		/* None was found, so create an entry. */
310		i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
311		if (i == NULL)
312			return ENOMEM;
313		if (cntp == NULL)
314			i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ;
315		else
316			i->cntp = cntp;
317		if (name != NULL && *name != '\0') {
318			/* XXX needs abstraction. Too error phrone. */
319			intrname = intrnames + (irq + INTRCNT_ISA_IRQ) *
320			    INTRNAME_LEN;
321			memset(intrname, ' ', INTRNAME_LEN - 1);
322			bcopy(name, intrname, strlen(name));
323		}
324		errcode = intr_event_create(&i->event, (void *)vector, 0,
325		    (void (*)(void *))ia64_send_eoi, "intr:");
326		if (errcode) {
327			free(i, M_DEVBUF);
328			return errcode;
329		}
330
331		mtx_lock_spin(&ia64_intrs_lock);
332		ia64_intrs[vector] = i;
333		mtx_unlock_spin(&ia64_intrs_lock);
334	}
335
336	/* Second, add this handler. */
337	errcode = intr_event_add_handler(i->event, name, handler, arg,
338	    intr_priority(flags), flags, cookiep);
339	if (errcode)
340		return errcode;
341
342	return (sapic_enable(irq, vector));
343}
344
345int
346ia64_teardown_intr(void *cookie)
347{
348
349	return (intr_event_remove_handler(cookie));
350}
351
352void
353ia64_dispatch_intr(void *frame, unsigned long vector)
354{
355	struct ia64_intr *i;
356	struct intr_event *ie;			/* our interrupt event */
357	struct intr_handler *ih;
358	int error, thread;
359
360	/*
361	 * Find the interrupt thread for this vector.
362	 */
363	i = ia64_intrs[vector];
364	if (i == NULL)
365		return;			/* no event for this vector */
366
367	if (i->cntp)
368		atomic_add_long(i->cntp, 1);
369
370	ie = i->event;
371	KASSERT(ie != NULL, ("interrupt vector without an event"));
372
373	/*
374	 * As an optimization, if an event has no handlers, don't
375	 * schedule it to run.
376	 */
377	if (TAILQ_EMPTY(&ie->ie_handlers))
378		return;
379
380	/*
381	 * Execute all fast interrupt handlers directly without Giant.  Note
382	 * that this means that any fast interrupt handler must be MP safe.
383	 */
384	thread = 0;
385	critical_enter();
386	TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
387		if (!(ih->ih_flags & IH_FAST)) {
388			thread = 1;
389			continue;
390		}
391		CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
392		    ih->ih_handler, ih->ih_argument, ih->ih_name);
393		ih->ih_handler(ih->ih_argument);
394	}
395	critical_exit();
396
397	if (thread) {
398		error = intr_event_schedule_thread(ie);
399		KASSERT(error == 0, ("got an impossible stray interrupt"));
400	} else
401		ia64_send_eoi(vector);
402}
403
404#ifdef DDB
405
406static void
407db_show_vector(int vector)
408{
409	int irq, i;
410
411	irq = vector - IA64_HARDWARE_IRQ_BASE;
412	for (i = 0; i < ia64_sapic_count; i++) {
413		struct sapic *sa = ia64_sapics[i];
414		if (irq >= sa->sa_base && irq <= sa->sa_limit)
415			sapic_print(sa, irq - sa->sa_base);
416	}
417}
418
419DB_SHOW_COMMAND(irq, db_show_irq)
420{
421	int vector;
422
423	if (have_addr) {
424		vector = ((addr >> 4) % 16) * 10 + (addr % 16);
425		db_show_vector(vector);
426	} else {
427		for (vector = IA64_HARDWARE_IRQ_BASE;
428		     vector < IA64_HARDWARE_IRQ_BASE + 64; vector++)
429			db_show_vector(vector);
430	}
431}
432
433#endif
434