1/* $NetBSD: interrupt.c,v 1.5 2010/12/20 00:25:35 matt Exp $ */
2
3/*-
4 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
5 * All rights reserved.
6 *
7 * Authors: Keith Bostic, Chris G. Demetriou
8 *
9 * Permission to use, copy, modify and distribute this software and
10 * its documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
14 *
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Carnegie Mellon requests users of this software to return to
20 *
21 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22 *  School of Computer Science
23 *  Carnegie Mellon University
24 *  Pittsburgh PA 15213-3890
25 *
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
28 */
29/*-
30 * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
31 * Redistribute and modify at will, leaving only this additional copyright
32 * notice.
33 */
34
35#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
36__KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.5 2010/12/20 00:25:35 matt Exp $");
37
38#include "opt_ddb.h"
39
40#include <sys/param.h>
41#include <sys/evcnt.h>
42#include <sys/lwp.h>
43#include <sys/proc.h>
44#include <sys/malloc.h>
45#include <sys/sched.h>
46
47#include <machine/clock.h>
48#include <machine/cpu.h>
49#include <machine/cpufunc.h>
50#include <machine/fpu.h>
51#include <machine/frame.h>
52#include <machine/intr.h>
53#include <machine/md_var.h>
54#include <machine/sapicvar.h>
55#include <machine/smp.h>
56#include <machine/userret.h>
57
58#ifdef DDB
59#include <ddb/ddb.h>
60#endif
61
62
63static void ia64_intr_eoi(void *);
64static void ia64_intr_mask(void *);
65#if 0
66static void ia64_intr_unmask(void *);
67#endif
68static int ia64_dispatch_intr(void *, u_int);
69
70#ifdef DDB
71void db_print_vector(u_int, int);
72#endif
73
74
75int
76interrupt(uint64_t vector, struct trapframe *tf)
77{
78	struct cpu_info *ci = curcpu();
79	volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
80	uint64_t adj, clk, itc;
81	int64_t delta;
82	uint8_t inta;
83	int count, handled = 0;
84
85	ia64_set_fpsr(IA64_FPSR_DEFAULT);
86
87	ci->ci_intrdepth++;
88	ci->ci_data.cpu_nintr++;
89
90 next:
91	/*
92	 * Handle ExtINT interrupts by generating an INTA cycle to
93	 * read the vector.
94	 */
95	if (vector == 0) {
96		inta = ib->ib_inta;
97		printf("ExtINT interrupt: vector=%u\n", (int)inta);
98		if (inta == 15) {
99			__asm __volatile("mov cr.eoi = r0;; srlz.d");
100			goto stray;
101		}
102		vector = (int)inta;
103	} else if (vector == 15)
104		goto stray;
105
106	if (vector == CLOCK_VECTOR) {/* clock interrupt */
107		itc = ia64_get_itc();
108
109		adj = ci->ci_clockadj;
110		clk = ci->ci_clock;
111		delta = itc - clk;
112		count = 0;
113		while (delta >= ia64_clock_reload) {
114			/* Only the BSP runs the real clock */
115			if (ci->ci_cpuid == 0)
116				hardclock((struct clockframe *)tf);
117			else
118				panic("CLOCK_VECTOR occur on not cpu0");
119			delta -= ia64_clock_reload;
120			clk += ia64_clock_reload;
121			count++;
122			handled = 1;
123		}
124		ia64_set_itm(ia64_get_itc() + ia64_clock_reload - adj);
125		if (count > 0) {
126			if (delta > (ia64_clock_reload >> 3)) {
127				adj = ia64_clock_reload >> 4;
128			} else
129				adj = 0;
130		} else
131			adj = 0;
132		ci->ci_clock = clk;
133		ci->ci_clockadj = adj;
134		ia64_srlz_d();
135
136#ifdef MULTIPROCESSOR
137	} else if (vector == ipi_vector[IPI_AST]) {
138		asts[PCPU_GET(cpuid)]++;
139		CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
140	} else if (vector == ipi_vector[IPI_HIGH_FP]) {
141		struct thread *thr = PCPU_GET(fpcurthread);
142
143		if (thr != NULL) {
144			mtx_lock_spin(&thr->td_md.md_highfp_mtx);
145			save_high_fp(&thr->td_pcb->pcb_high_fp);
146			thr->td_pcb->pcb_fpcpu = NULL;
147			PCPU_SET(fpcurthread, NULL);
148			mtx_unlock_spin(&thr->td_md.md_highfp_mtx);
149		}
150	} else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
151		rdvs[PCPU_GET(cpuid)]++;
152		CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));
153		enable_intr();
154		smp_rendezvous_action();
155		disable_intr();
156	} else if (vector == ipi_vector[IPI_STOP]) {
157		cpumask_t mybit = PCPU_GET(cpumask);
158
159		savectx(PCPU_PTR(pcb));
160		atomic_set_int(&stopped_cpus, mybit);
161		while ((started_cpus & mybit) == 0)
162			cpu_spinwait();
163		atomic_clear_int(&started_cpus, mybit);
164		atomic_clear_int(&stopped_cpus, mybit);
165	} else if (vector == ipi_vector[IPI_PREEMPT]) {
166		CTR1(KTR_SMP, "IPI_PREEMPT, cpuid=%d", PCPU_GET(cpuid));
167		__asm __volatile("mov cr.eoi = r0;; srlz.d");
168		enable_intr();
169		sched_preempt(curthread);
170		disable_intr();
171		goto stray;
172#endif
173	} else {
174		ci->ci_intrdepth++;
175		handled = ia64_dispatch_intr(tf, vector);
176		ci->ci_intrdepth--;
177	}
178
179	__asm __volatile("mov cr.eoi = r0;; srlz.d");
180	vector = ia64_get_ivr();
181	if (vector != 15)
182		goto next;
183
184stray:
185	if (TRAPF_USERMODE(tf)) {
186		enable_intr();
187		userret(curlwp);
188		do_ast(tf);
189	}
190	ci->ci_intrdepth--;
191	return handled;
192}
193
194
195/*
196 * Hardware irqs have vectors starting at this offset.
197 */
198#define IA64_HARDWARE_IRQ_BASE	0x20
199
200struct ia64_intrhand {
201	int (*ih_func)(void *);
202	void *ih_arg;
203	LIST_ENTRY(ia64_intrhand) ih_q;
204	int ih_level;
205	int ih_irq;
206};
207struct ia64_intr {
208	u_int irq;
209	struct sapic *sapic;
210	int type;
211
212	LIST_HEAD(, ia64_intrhand) intr_q;
213
214	char evname[32];
215	struct evcnt evcnt;
216};
217
218static struct ia64_intr *ia64_intrs[256];
219
220
221static void
222ia64_intr_eoi(void *arg)
223{
224	u_int vector = (uintptr_t)arg;
225	struct ia64_intr *i;
226
227	i = ia64_intrs[vector];
228	if (i != NULL)
229		sapic_eoi(i->sapic, vector);
230}
231
232static void
233ia64_intr_mask(void *arg)
234{
235	u_int vector = (uintptr_t)arg;
236	struct ia64_intr *i;
237
238	i = ia64_intrs[vector];
239	if (i != NULL) {
240		sapic_mask(i->sapic, i->irq);
241		sapic_eoi(i->sapic, vector);
242	}
243}
244
245#if 0
246static void
247ia64_intr_unmask(void *arg)
248{
249	u_int vector = (uintptr_t)arg;
250	struct ia64_intr *i;
251
252	i = ia64_intrs[vector];
253	if (i != NULL)
254		sapic_unmask(i->sapic, i->irq);
255}
256#endif
257
258void *
259intr_establish(int irq, int type, int level, int (*func)(void *), void *arg)
260{
261	struct ia64_intr *i;
262	struct ia64_intrhand *ih;
263	struct sapic *sa;
264	u_int vector;
265
266	/* Get the I/O SAPIC that corresponds to the IRQ. */
267	sa = sapic_lookup(irq);
268	if (sa == NULL)
269		return NULL;
270
271	switch (type) {
272	case IST_EDGE:
273	case IST_LEVEL:
274		break;
275
276	default:
277		return NULL;
278	}
279
280	/*
281	 * XXX - There's a priority implied by the choice of vector.
282	 * We should therefore relate the vector to the interrupt type.
283	 */
284	vector = irq + IA64_HARDWARE_IRQ_BASE;
285
286	i = ia64_intrs[vector];
287	if (i == NULL) {
288		i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
289		if (i == NULL)
290			return NULL;
291		i->irq = irq;
292		i->sapic = sa;
293		i->type = type;
294		LIST_INIT(&i->intr_q);
295		snprintf(i->evname, sizeof(i->evname), "irq %d", irq);
296		evcnt_attach_dynamic(&i->evcnt, EVCNT_TYPE_INTR, NULL,
297		    "iosapic", i->evname);
298		ia64_intrs[vector] = i;
299
300		sapic_config_intr(irq, type);
301		sapic_enable(i->sapic, irq, vector);
302	} else
303		if (i->type != type)
304			return NULL;
305
306	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
307	if (ih == NULL)
308		return NULL;
309	ih->ih_func = func;
310	ih->ih_arg = arg;
311	ih->ih_level = level;
312	ih->ih_irq = irq;
313	LIST_INSERT_HEAD(&i->intr_q, ih, ih_q);
314
315	return ih;
316}
317
318void
319intr_disestablish(void *cookie)
320{
321	struct ia64_intr *i;
322	struct ia64_intrhand *ih = cookie;
323	u_int vector = ih->ih_irq + IA64_HARDWARE_IRQ_BASE;
324
325	i = ia64_intrs[vector];
326
327	LIST_REMOVE(ih, ih_q);
328	if (LIST_FIRST(&i->intr_q) == NULL) {
329		ia64_intr_mask((void *)(uintptr_t)vector);
330
331		ia64_intrs[vector] = NULL;
332		evcnt_detach(&i->evcnt);
333		free(i, M_DEVBUF);
334	}
335
336	free(ih, M_DEVBUF);
337}
338
339static int
340ia64_dispatch_intr(void *frame, u_int vector)
341{
342	struct ia64_intr *i;
343	struct ia64_intrhand *ih;
344	int handled = 0;
345
346	/*
347	 * Find the interrupt thread for this vector.
348	 */
349	i = ia64_intrs[vector];
350	KASSERT(i != NULL);
351
352	i->evcnt.ev_count++;
353
354	LIST_FOREACH(ih, &i->intr_q, ih_q) {
355		if (__predict_false(ih->ih_func == NULL))
356			printf("%s: spurious interrupt (irq = %d)\n",
357			    __func__, ih->ih_irq);
358		else if (__predict_true((*ih->ih_func)(ih->ih_arg)))
359			handled = 1;
360	}
361	ia64_intr_eoi((void *)(uintptr_t)vector);
362
363	return handled;
364}
365
366#ifdef DDB
367void
368db_print_vector(u_int vector, int always)
369{
370	struct ia64_intr *i;
371
372	i = ia64_intrs[vector];
373	if (i != NULL) {
374		db_printf("vector %u (%p): ", vector, i);
375		sapic_print(i->sapic, i->irq);
376	} else if (always)
377		db_printf("vector %u: unassigned\n", vector);
378}
379#endif
380