interrupt.c revision 179256
1/* $FreeBSD: head/sys/ia64/ia64/interrupt.c 179256 2008-05-23 19:53:50Z marcel $ */
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/sched.h>
51#include <sys/smp.h>
52#include <sys/sysctl.h>
53#include <sys/syslog.h>
54
55#include <machine/clock.h>
56#include <machine/cpu.h>
57#include <machine/fpu.h>
58#include <machine/frame.h>
59#include <machine/intr.h>
60#include <machine/md_var.h>
61#include <machine/pcb.h>
62#include <machine/reg.h>
63#include <machine/sapicvar.h>
64#include <machine/smp.h>
65
66#ifdef EVCNT_COUNTERS
67struct evcnt clock_intr_evcnt;	/* event counter for clock intrs. */
68#else
69#include <sys/interrupt.h>
70#include <machine/intrcnt.h>
71#endif
72
73#ifdef DDB
74#include <ddb/ddb.h>
75#endif
76
77#ifdef SMP
78extern int mp_ipi_test;
79#endif
80
81static void ia64_dispatch_intr(void *, u_int);
82
83static void
84dummy_perf(unsigned long vector, struct trapframe *tf)
85{
86	printf("performance interrupt!\n");
87}
88
89void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf;
90
91static unsigned int ints[MAXCPU];
92SYSCTL_OPAQUE(_debug, OID_AUTO, ints, CTLFLAG_RW, &ints, sizeof(ints), "IU",
93    "");
94
95static unsigned int clks[MAXCPU];
96#ifdef SMP
97SYSCTL_OPAQUE(_debug, OID_AUTO, clks, CTLFLAG_RW, &clks, sizeof(clks), "IU",
98    "");
99#else
100SYSCTL_INT(_debug, OID_AUTO, clks, CTLFLAG_RW, clks, 0, "");
101#endif
102
103#ifdef SMP
104static unsigned int asts[MAXCPU];
105SYSCTL_OPAQUE(_debug, OID_AUTO, asts, CTLFLAG_RW, &asts, sizeof(asts), "IU",
106    "");
107
108static unsigned int rdvs[MAXCPU];
109SYSCTL_OPAQUE(_debug, OID_AUTO, rdvs, CTLFLAG_RW, &rdvs, sizeof(rdvs), "IU",
110    "");
111#endif
112
113SYSCTL_NODE(_debug, OID_AUTO, clock, CTLFLAG_RW, 0, "clock statistics");
114
115static int adjust_edges = 0;
116SYSCTL_INT(_debug_clock, OID_AUTO, adjust_edges, CTLFLAG_RD,
117    &adjust_edges, 0, "Number of times ITC got more than 12.5% behind");
118
119static int adjust_excess = 0;
120SYSCTL_INT(_debug_clock, OID_AUTO, adjust_excess, CTLFLAG_RD,
121    &adjust_excess, 0, "Total number of ignored ITC interrupts");
122
123static int adjust_lost = 0;
124SYSCTL_INT(_debug_clock, OID_AUTO, adjust_lost, CTLFLAG_RD,
125    &adjust_lost, 0, "Total number of lost ITC interrupts");
126
127static int adjust_ticks = 0;
128SYSCTL_INT(_debug_clock, OID_AUTO, adjust_ticks, CTLFLAG_RD,
129    &adjust_ticks, 0, "Total number of ITC interrupts with adjustment");
130
131void
132interrupt(struct trapframe *tf)
133{
134	struct thread *td;
135	volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
136	uint64_t adj, clk, itc;
137	int64_t delta;
138	u_int vector;
139	int count;
140	uint8_t inta;
141
142	ia64_set_fpsr(IA64_FPSR_DEFAULT);
143
144	td = curthread;
145
146	vector = tf->tf_special.ifa;
147
148 next:
149	/*
150	 * Handle ExtINT interrupts by generating an INTA cycle to
151	 * read the vector.
152	 */
153	if (vector == 0) {
154		inta = ib->ib_inta;
155		printf("ExtINT interrupt: vector=%u\n", (int)inta);
156		if (inta == 15) {
157			__asm __volatile("mov cr.eoi = r0;; srlz.d");
158			goto stray;
159		}
160		vector = (int)inta;
161	} else if (vector == 15)
162		goto stray;
163
164	if (vector == CLOCK_VECTOR) {/* clock interrupt */
165		/* CTR0(KTR_INTR, "clock interrupt"); */
166
167		itc = ia64_get_itc();
168
169		PCPU_INC(cnt.v_intr);
170#ifdef EVCNT_COUNTERS
171		clock_intr_evcnt.ev_count++;
172#else
173		intrcnt[INTRCNT_CLOCK]++;
174#endif
175		clks[PCPU_GET(cpuid)]++;
176
177		critical_enter();
178
179		adj = PCPU_GET(clockadj);
180		clk = PCPU_GET(clock);
181		delta = itc - clk;
182		count = 0;
183		while (delta >= ia64_clock_reload) {
184			/* Only the BSP runs the real clock */
185			if (PCPU_GET(cpuid) == 0)
186				hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
187			else
188				hardclock_cpu(TRAPF_USERMODE(tf));
189			if (profprocs != 0)
190				profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
191			statclock(TRAPF_USERMODE(tf));
192			delta -= ia64_clock_reload;
193			clk += ia64_clock_reload;
194			if (adj != 0)
195				adjust_ticks++;
196			count++;
197		}
198		ia64_set_itm(ia64_get_itc() + ia64_clock_reload - adj);
199		if (count > 0) {
200			adjust_lost += count - 1;
201			if (delta > (ia64_clock_reload >> 3)) {
202				if (adj == 0)
203					adjust_edges++;
204				adj = ia64_clock_reload >> 4;
205			} else
206				adj = 0;
207		} else {
208			adj = 0;
209			adjust_excess++;
210		}
211		PCPU_SET(clock, clk);
212		PCPU_SET(clockadj, adj);
213		critical_exit();
214		ia64_srlz_d();
215
216#ifdef SMP
217	} else if (vector == ipi_vector[IPI_AST]) {
218		asts[PCPU_GET(cpuid)]++;
219		CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
220	} else if (vector == ipi_vector[IPI_HIGH_FP]) {
221		struct thread *thr = PCPU_GET(fpcurthread);
222		if (thr != NULL) {
223			mtx_lock_spin(&thr->td_md.md_highfp_mtx);
224			save_high_fp(&thr->td_pcb->pcb_high_fp);
225			thr->td_pcb->pcb_fpcpu = NULL;
226			PCPU_SET(fpcurthread, NULL);
227			mtx_unlock_spin(&thr->td_md.md_highfp_mtx);
228		}
229	} else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
230		rdvs[PCPU_GET(cpuid)]++;
231		CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));
232		enable_intr();
233		smp_rendezvous_action();
234		disable_intr();
235	} else if (vector == ipi_vector[IPI_STOP]) {
236		cpumask_t mybit = PCPU_GET(cpumask);
237
238		savectx(PCPU_PTR(pcb));
239		atomic_set_int(&stopped_cpus, mybit);
240		while ((started_cpus & mybit) == 0)
241			cpu_spinwait();
242		atomic_clear_int(&started_cpus, mybit);
243		atomic_clear_int(&stopped_cpus, mybit);
244	} else if (vector == ipi_vector[IPI_TEST]) {
245		CTR1(KTR_SMP, "IPI_TEST, cpuid=%d", PCPU_GET(cpuid));
246		mp_ipi_test++;
247	} else if (vector == ipi_vector[IPI_PREEMPT]) {
248		CTR1(KTR_SMP, "IPI_PREEMPT, cpuid=%d", PCPU_GET(cpuid));
249		__asm __volatile("mov cr.eoi = r0;; srlz.d");
250		enable_intr();
251		sched_preempt(curthread);
252		disable_intr();
253		goto stray;
254#endif
255	} else {
256		ints[PCPU_GET(cpuid)]++;
257		atomic_add_int(&td->td_intr_nesting_level, 1);
258		ia64_dispatch_intr(tf, vector);
259		atomic_subtract_int(&td->td_intr_nesting_level, 1);
260	}
261
262	__asm __volatile("mov cr.eoi = r0;; srlz.d");
263	vector = ia64_get_ivr();
264	if (vector != 15)
265		goto next;
266
267stray:
268	if (TRAPF_USERMODE(tf)) {
269		enable_intr();
270		userret(td, tf);
271		mtx_assert(&Giant, MA_NOTOWNED);
272		do_ast(tf);
273	}
274}
275
276/*
277 * Hardware irqs have vectors starting at this offset.
278 */
279#define IA64_HARDWARE_IRQ_BASE	0x20
280
281struct ia64_intr {
282	struct intr_event *event;	/* interrupt event */
283	volatile long *cntp;		/* interrupt counter */
284	struct sapic *sapic;
285	u_int	irq;
286};
287
288static struct ia64_intr *ia64_intrs[256];
289
290static void
291ia64_intr_eoi(void *arg)
292{
293	u_int vector = (uintptr_t)arg;
294	struct ia64_intr *i;
295
296	i = ia64_intrs[vector];
297	if (i != NULL)
298		sapic_eoi(i->sapic, vector);
299}
300
301static void
302ia64_intr_mask(void *arg)
303{
304	u_int vector = (uintptr_t)arg;
305	struct ia64_intr *i;
306
307	i = ia64_intrs[vector];
308	if (i != NULL) {
309		sapic_mask(i->sapic, i->irq);
310		sapic_eoi(i->sapic, vector);
311	}
312}
313
314static void
315ia64_intr_unmask(void *arg)
316{
317	u_int vector = (uintptr_t)arg;
318	struct ia64_intr *i;
319
320	i = ia64_intrs[vector];
321	if (i != NULL)
322		sapic_unmask(i->sapic, i->irq);
323}
324
325int
326ia64_setup_intr(const char *name, int irq, driver_filter_t filter,
327    driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
328{
329	struct ia64_intr *i;
330	struct sapic *sa;
331	char *intrname;
332	u_int vector;
333	int error;
334
335	/* Get the I/O SAPIC that corresponds to the IRQ. */
336	sa = sapic_lookup(irq);
337	if (sa == NULL)
338		return (EINVAL);
339
340	/*
341	 * XXX - There's a priority implied by the choice of vector.
342	 * We should therefore relate the vector to the interrupt type.
343	 */
344	vector = irq + IA64_HARDWARE_IRQ_BASE;
345
346	i = ia64_intrs[vector];
347	if (i == NULL) {
348		i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
349		if (i == NULL)
350			return (ENOMEM);
351
352		error = intr_event_create(&i->event, (void *)(uintptr_t)vector,
353		    0, irq, ia64_intr_mask, ia64_intr_unmask, ia64_intr_eoi,
354		    NULL, "irq%u:", irq);
355		if (error) {
356			free(i, M_DEVBUF);
357			return (error);
358		}
359
360		if (!atomic_cmpset_ptr(&ia64_intrs[vector], NULL, i)) {
361			intr_event_destroy(i->event);
362			free(i, M_DEVBUF);
363			i = ia64_intrs[vector];
364		} else {
365			i->sapic = sa;
366			i->irq = irq;
367
368			i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ;
369			if (name != NULL && *name != '\0') {
370				/* XXX needs abstraction. Too error prone. */
371				intrname = intrnames +
372				    (irq + INTRCNT_ISA_IRQ) * INTRNAME_LEN;
373				memset(intrname, ' ', INTRNAME_LEN - 1);
374				bcopy(name, intrname, strlen(name));
375			}
376
377			sapic_enable(i->sapic, irq, vector);
378		}
379	}
380
381	error = intr_event_add_handler(i->event, name, filter, handler, arg,
382	    intr_priority(flags), flags, cookiep);
383	return (error);
384}
385
386int
387ia64_teardown_intr(void *cookie)
388{
389
390	return (intr_event_remove_handler(cookie));
391}
392
393static void
394ia64_dispatch_intr(void *frame, u_int vector)
395{
396	struct ia64_intr *i;
397	struct intr_event *ie;			/* our interrupt event */
398
399	/*
400	 * Find the interrupt thread for this vector.
401	 */
402	i = ia64_intrs[vector];
403	KASSERT(i != NULL, ("%s: unassigned vector", __func__));
404
405	(*i->cntp)++;
406
407	ie = i->event;
408	KASSERT(ie != NULL, ("%s: interrupt without event", __func__));
409
410	if (intr_event_handle(ie, frame) != 0) {
411		/*
412		 * XXX: The pre-INTR_FILTER code didn't mask stray
413		 * interrupts.
414		 */
415		ia64_intr_mask((void *)(uintptr_t)vector);
416		log(LOG_ERR, "stray irq%u\n", i->irq);
417	}
418}
419
420#ifdef DDB
421
422static void
423db_print_vector(u_int vector, int always)
424{
425	struct ia64_intr *i;
426
427	i = ia64_intrs[vector];
428	if (i != NULL) {
429		db_printf("vector %u (%p): ", vector, i);
430		sapic_print(i->sapic, i->irq);
431	} else if (always)
432		db_printf("vector %u: unassigned\n", vector);
433}
434
435DB_SHOW_COMMAND(vector, db_show_vector)
436{
437	u_int vector;
438
439	if (have_addr) {
440		vector = ((addr >> 4) % 16) * 10 + (addr % 16);
441		if (vector >= 256)
442			db_printf("error: vector %u not in range [0..255]\n",
443			    vector);
444		else
445			db_print_vector(vector, 1);
446	} else {
447		for (vector = 0; vector < 256; vector++)
448			db_print_vector(vector, 0);
449	}
450}
451
452#endif
453